home *** CD-ROM | disk | FTP | other *** search
- /*
- ** Copyright 1989 BBN Systems and Technologies Corporation.
- ** All Rights Reserved.
- ** This is free software, and may be distributed under the terms of the
- ** GNU Public License; see the file COPYING for more details.
- **
- ** Main driver for CODA server.
- */
- #define MAINLINE
- #include "server.h"
- #include <pwd.h>
- #include <signal.h>
- #include <setjmp.h>
- #include <sys/stat.h>
- #include <sys/socket.h>
- #include <netinet/in.h>
- #include <netdb.h>
- #ifdef RCSID
- static char RCS[] =
- "$Header: server.c,v 2.0 90/03/23 14:41:55 rsalz Exp $";
- #endif /* RCSID */
-
-
- /*
- ** A command has a text name, an internal value, and a help message.
- */
- typedef struct _TABLE {
- char *Name;
- COMMAND Value;
- char *Help;
- } TABLE;
-
- char *LogFile = LOGFILE; /* Name of the log file */
- char UnknownHost[] = GUESTHOST; /* For unknown hosts */
-
- STATIC jmp_buf Context; /* When the bell rings */
- STATIC BOOL LoggedIn; /* Did we get a USER command? */
- STATIC BOOL Testing; /* Are we in test mode? */
-
- STATIC TABLE Commands[] = { /* List of commands */
- { "GOTO", CMDgoto, "Change to specified directory" },
- { "EXIT", CMDquit, "Shut down server" },
- { "HELP", CMDhelp, "Print this status report" },
- { "HOST", CMDhost, "Specify name of destination host" },
- { "MESG", CMDmesg, "Send message to log file" },
- { "LIST", CMDlist, "List status of files [in block]" },
- { "QUIT", CMDquit, "Shut down server" },
- { "READ", CMDread, "Read control file, name optional" },
- { "ROOT", CMDroot, "Set root for relative pathnames" },
- { "SEND", CMDsend, "Start file-sending protocol" },
- { "USER", CMDuser, "Log in a specified user" },
- { "", CMD_time, NULL },
- { NULL }
- };
-
-
-
- /*
- ** Return a perror-style string.
- */
- char *
- strerror(e)
- int e;
- {
- extern int sys_nerr;
- extern char *sys_errlist[];
- char buff[20];
-
- if (e < 0 || e > sys_nerr) {
- (void)sprintf(buff, "Error code %d\n", e);
- return buff;
- }
- return sys_errlist[e];
- }
-
-
-
- /*
- ** Send a message saying a command was successful.
- */
- void
- Ack(p)
- char *p;
- {
- (void)printf("ACK-%s\r\n", p ? p : "Done");
- (void)fflush(stdout);
- }
-
-
- /*
- ** Send a message saying that a command failed.
- */
- void
- Nack(p)
- char *p;
- {
- (void)printf("NAK-%s\r\n", p ? p : strerror(errno));
- (void)fflush(stdout);
- }
-
-
- /*
- ** Send an information message. The client shoujld pass these on to
- ** the user.
- */
- void
- Message(p)
- char *p;
- {
- (void)printf("INF %s\r\n", p ? p : "Never mind");
- }
-
-
- /*
- ** Send a data message. This is for the client program.
- */
- void
- Data(p)
- char *p;
- {
- if (p)
- (void)printf("DAT %s\r\n", p);
- }
-
-
-
- /*
- ** When that bell rings, get out of here!
- */
- STATIC CATCHER
- AlarmCatch()
- {
- longjmp(Context, 1);
- /* NOTREACHED */
- }
-
-
- /*
- ** Read a line from the client. Quit if it times out. Otherwise
- ** parse the first word as a command, stuff the rest of the line as
- ** a possible argument to the command, and return the command's value.
- */
- STATIC COMMAND
- ReadLine(arg, size)
- char *arg;
- int size;
- {
- register char *p;
- register char *q;
- register TABLE *T;
- char buff[SIZE];
-
- if (setjmp(Context) == 1)
- return CMD_time;
-
- for ( ; ; ) {
- /* Timed-out read. */
- (void)alarm(TIMEOUT);
- p = fgets(buff, sizeof buff, stdin);
- (void)alarm(0);
- if (p == NULL)
- return CMDquit;
-
- /* Kill the terminator. */
- if ((p = strchr(buff, '\r')) || (p = strchr(buff, '\n')))
- *p = '\0';
-
- /* Skip whitespace, ignore totally blank lines. */
- for (p = buff; *p && WHITE(*p); p++)
- ;
- if (*p == '\0')
- continue;
-
- /* Find first word. */
- for (q = p; *q && !WHITE(*q); q++)
- ;
-
- /* Snip off first word, copy rest of line to argument. */
- if (*q) {
- for (*q = '\0'; *++q && WHITE(*q); )
- ;
- (void)strncpy(arg, q, size);
- arg[size - 1] = '\0';
- }
- else
- arg[0] = '\0';
- Uppercase(p);
-
- /* Find first word in the command table. */
- for (T = Commands; T->Name; T++)
- if (EQ(T->Name, p))
- return T->Value;
-
- Nack("Unknown command");
- }
- }
-
-
-
-
- /*
- ** Get the name of the host where the client it.
- */
- STATIC void
- GetClientHostname()
- {
- struct hostent *hp;
- struct sockaddr_in venial;
- char buff[SIZE];
- int size;
-
- if (isatty(0))
- /* Obviously a local call... */
- TheHost = gethostname(buff, sizeof buff) < 0 ? NULL : buff;
- else {
- size = sizeof venial;
- if (getpeername(0, (struct sockaddr *)&venial, &size) < 0)
- TheHost = NULL;
- else {
- hp = gethostbyaddr((char *)&venial.sin_addr,
- sizeof venial.sin_addr, AF_INET);
- TheHost = hp ? hp->h_name : NULL;
- }
- }
-
- TheHost = TheHost ? COPY(TheHost) : COPY(UnknownHost);
- Uppercase(TheHost);
- }
-
-
- /*
- ** Read a CODA control file to see if its valid.
- */
- STATIC void
- CheckCodafile(p)
- char *p;
- {
- ResetStorage();
- if (!yyopen(TRUE, p)) {
- Nack((char *)NULL);
- exit(1);
- }
- if (yyparse() != BADPARSE && !HaveErrors)
- exit(0);
- Nack("Syntax errors found");
- exit(1);
- }
-
-
- /*
- ** Verify a user's name and password.
- */
- STATIC void
- Login(p)
- char *p;
- {
- static char BAD[] = "Bad username or password";
- struct passwd *pwd;
- char *pass;
- char buff[SIZE];
-
- /* Split at the space into a name:password pair. */
- if ((pass = strchr(p, ' ')) == NULL) {
- Nack(BAD);
- return;
- }
- *pass++ = '\0';
-
- /* Valid name with the right password? */
- if ((pwd = getpwnam(p)) == NULL) {
- Nack(BAD);
- return;
- }
- if (!EQ(pwd->pw_passwd, crypt(pass, pwd->pw_passwd))) {
- Nack(BAD);
- return;
- }
-
- /* Change identity. */
- if (initgroups(p, pwd->pw_gid) < 0 || setuid(pwd->pw_uid) < 0) {
- Nack((char *)NULL);
- return;
- }
-
- /* Done; log and acknowledge it. */
- LoggedIn = TRUE;
- (void)sprintf(buff, "Logged in %s", p);
- LogText(buff);
- Ack(buff);
- }
-
-
- /*
- ** Print a usage message and exit.
- */
- STATIC void
- Usage()
- {
- (void)fprintf(stderr, "Usage: codaserver %s\n",
- "[-c] [-lLogfile] [-rCodafile] [-t]");
- exit(1);
- }
-
-
- /* ARGSUSED1 */
- main(ac, av)
- int ac;
- char *av[];
- {
- static char LOGIN[] = "Login first";
- register BOOL DidRead;
- register char *p;
- register COMMAND C;
- register int i;
- register TABLE *T;
- char arg[SIZE];
-
- /* Parse JCL. */
- while ((i = getopt(ac, av, "cl:r:t")) != EOF)
- switch (i) {
- default:
- Usage();
- /* NOTREACHED */
- case 'c':
- Copyright();
- exit(0);
- /* NOTREACHED */
- case 'l':
- LogFile = optarg;
- break;
- case 'r':
- CheckCodafile(optarg);
- /* NOTREACHED */
- case 't':
- /* Play it safe... */
- if (isatty(0)) {
- Testing = TRUE;
- LoggedIn = TRUE;
- (void)printf("Testing...\n");
- }
- break;
- }
-
- /* Check for other arguments. */
- ac -= optind;
- av += optind;
- if (ac)
- Usage();
-
- if (!Testing) {
- /* Set up IO descriptors, be robust in the face of varying inetd's. */
- (void)close(1);
- (void)close(2);
- (void)dup(0);
- (void)dup(0);
- }
-
- /* Do various initializations. */
- GetClientHostname();
- (void)signal(SIGALRM, AlarmCatch);
- ResetStorage();
- LogOpen();
-
- /* Tell client we're here. */
- (void)sprintf(arg, "Hello %s; how are you today?", TheHost);
- Ack(arg);
-
- /* Read and dispatch loop. */
- DidRead = FALSE;
- while ((C = ReadLine(arg, sizeof arg)) != CMDquit)
- switch (C) {
- default:
- Nack("Unimplemented command");
- break;
-
- case CMDgoto:
- if (!LoggedIn)
- Nack(LOGIN);
- else if (arg[0] == '\0')
- Nack("Directory missing");
- else if (chdir(arg) < 0)
- Nack((char *)NULL);
- else
- Ack((char *)NULL);
- break;
-
- case CMDhelp:
- for (T = Commands; T->Name; T++)
- if (T->Help) {
- (void)sprintf(arg, "%s\t%s", T->Name, T->Help);
- Message(arg);
- }
- Ack((char *)NULL);
- break;
-
- case CMDhost:
- if (!Testing)
- Nack("I already know who you are");
- else {
- if (TheHost) {
- free(TheHost);
- TheHost = NULL;
- }
- Uppercase(arg);
- if (HostWasDeclared(arg)) {
- TheHost = COPY(arg);
- Ack((char *)NULL);
- }
- else
- Nack("Host undefined");
- }
- break;
-
- case CMDlist:
- if (!LoggedIn)
- Nack(LOGIN);
- else if (TheHost == NULL)
- Nack("No host specified");
- else if (!DidRead)
- Nack("Use READ first");
- else
- /* ListFiles() does its own Ack or Nack. */
- ListFiles(arg);
- break;
-
- case CMDmesg:
- LogText(arg);
- Ack((char *)NULL);
- break;
-
- case CMDread:
- if (!LoggedIn)
- Nack(LOGIN);
- else {
- Rooted = FALSE;
- AllowBinaries = TRUE;
- ResetStorage();
- DidRead = FALSE;
- p = arg[0] ? arg : CONTROLFILE;
- if (!yyopen(FALSE, p))
- Nack((char *)NULL);
- else {
- if (yyparse() == BADPARSE || HaveErrors)
- Nack("Can't parse file");
- else {
- LogReadfile(p);
- if (!HostWasDeclared(TheHost)) {
- (void)sprintf(arg, "Host \"%s\" not in the file.",
- TheHost);
- Message(arg);
- }
- Ack((char *)NULL);
- DidRead = TRUE;
- }
- yyclose();
- }
- }
- break;
-
- case CMDroot:
- if (Rooted)
- Nack("Permission denied");
- else {
- if (TheRoot)
- free(TheRoot);
- TheRoot = arg[0] ? COPY(arg) : NULL;
- Ack((char *)NULL);
- }
- break;
-
- case CMDsend:
- if (!LoggedIn)
- Nack(LOGIN);
- else if (!DidRead)
- Nack("Use READ first");
- else if (arg[0])
- /* SendFile() does its own Ack or Nack. */
- SendFile(arg);
- else
- Nack("Filename missing");
- break;
-
- case CMDuser:
- /* Login() does its own Ack or Nack. */
- Login(arg);
- break;
-
- case CMD_time:
- LogClose("timeout");
- exit(0);
- /* NOTREACHED */
-
- }
-
- LogClose("quit");
- exit(0);
- /* NOTREACHED */
- }
-