/*
 * tacacs-server.c
 *   Implementation of a TACACS server for VMS.  Accepts a TACACS query
 *   from a client, and returns ACCEPTED or REJECTED.  (Change request
 *   transactions are refused.)
 *         
 *   Based upon tacacsd.c as found on ftp.cisco.com (written by Greg Satz), 
 *   and upon MULTINET_ROOT:[MULTINET.EXAMPLES]UPDECHOSERVER-STANDALONE.C.
 *   The original tacacsd.c is 
 *   "Copyright (c) 1989 by cisco Systems, Inc., All rights reserved."
 *
 *   Requires SYSNAM and SYSPRV privileges.  Requires VMS V5.4, because
 *   uses the $HASH_PASSWORD system service.  Requires MultiNet(TM) 
 *   from TGV, Inc.; building this under an inferior implementation of
 *   TCP/IP is left as an exercise.
 *
 * Last modified: 24-Dec-1990/al        Aaron Leonard <leonard@arizona.edu>
 *
 * 24-Dec-1990  al      Working on it again
 * 11-Nov-1990  al      First version
 *
 */

#include "includes.h"

main()
{
    LONG init_tacacs_uaf(), logerror(), process(), SYS$DCLEXH();
    void tacacs_exit_handler();

    LONG status, len;
    static int on = 1;
    char inbuf[TACACS_INBUF_SIZE];
    char outstring[BIG_STRING];
    struct tacacspkt *tp;
    struct servent *sp;
    unsigned short int sock;
    struct sockaddr_in sin;
    EXH_BLK exh_blk = {0, tacacs_exit_handler, 0, &status};

    status = SYS$DCLEXH(&exh_blk);
    iferr(status)
        exit(status);

    status = init_tacacs_uaf();
    iferr(status)
    {
        sprintf(outstring, "tacacs-server: can't init TACACS_UAF - status %X",
                status);
        logerror(outstring);
        exit(status);
    } 

    /* get the UDP port number of the TACACS server */
    sp = getservbyname("tacacs", "udp");
    if (sp == 0)
    {
        sp = malloc(sizeof(struct servent));
        sp->s_port = htons(TACACS_PORT);
    }

    /* make a socket on which to listen */
    sock = socket(AF_INET, SOCK_DGRAM, 0);
    if (sock < 0)
    {
        sprintf(outstring, "tacacs-server: socket - %s", vms_errno_string());
        logerror(outstring);
        exit(vaxc$errno);
    }

    /* set the "REUSEADDR" option on this socket */
    if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0)
    {
        sprintf(outstring, "tacacs-server: setsockopt - %s", 
                vms_errno_string());
        logerror(outstring);
        exit(vaxc$errno);
    }

    /* create a sockaddr_in for our port */
    sin.sin_family = AF_INET;
    sin.sin_addr.s_addr = INADDR_ANY;
    sin.sin_port = sp->s_port;

    /* bind to this address */
    if (bind(sock, &sin, sizeof(sin), 0) < 0)
    {
        sprintf(outstring, "tacacs-server: bind - %s", vms_errno_string());
        logerror(outstring);
        exit(vaxc$errno);
    }

    /* now just read from the net ... */

loop:
    len = sizeof(sin);
    status = recvfrom(sock, inbuf, sizeof(inbuf), 0, &sin, &len);
    if (status == 0)
        goto loop;
    if (status < 0)
    {
        sprintf(outstring, "tacacs-server: recvfrom - %s", vms_errno_string());
        logerror(outstring);
        exit(vaxc$errno);
    }

    /* try to interpret this packet as a TACACS packet, and sanity-check it */

    tp = (struct tacacspkt *) inbuf;
    if (tp->version != TA_VERSION)
    {
        sprintf(outstring, "tacacs-server: illegal version specified: %d", 
                tp->version);
        logerror(outstring);
        goto loop;
    }
    if (tp->type != TA_QUERY)
    {
        sprintf(outstring, "tacacs-server: illegal type specified: %d", 
                tp->type);
        logerror(outstring);
        goto loop;
    }

    /* now verify the packet */

    status = process(sock, &sin, tp);
    iferr(status)
    {
        sprintf(outstring, "tacacs-server: error processing query: %X");
        logerror(outstring);
    }
    goto loop;

}   /* main */

/* tacacs-server.c */
