/
Genesis-1.0p36-DEV/
Genesis-1.0p36-DEV/bin/
Genesis-1.0p36-DEV/doc/
Genesis-1.0p36-DEV/etc/
Genesis-1.0p36-DEV/src/data/
/*
// Full copyright information is available in the file ../doc/CREDITS
//
// RFC references: inverse name resolution--1293, 903 1035 - domain name system
//
// This is a quickie--full implementation can come later
//
//  -Brandon
*/

#define _BSD 44 /* For RS6000s. */

#include "defs.h"
#include <sys/types.h>
#ifdef __UNIX__
#include <sys/param.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <signal.h>
#endif
#include <fcntl.h>
#include <stdarg.h>
#include <ctype.h>
#include "net.h"
#include "util.h"
#include "dns.h"

/*
// -------------------------------------------------------------------------
// generic defines
// -------------------------------------------------------------------------
*/

/*#define LINE 255*/
#define NET_PORT 1153
#define MALLOC(type, num) ((type *) malloc((num) * sizeof(type)))
#define FREE(ptr)         free(ptr)
#define CONN_QUEUE 8     /* how many conns to backlog before the next read? */

#define WAIT_FOR_CONN 1

/*
// -------------------------------------------------------------------------
// structs, typedefs and globals
// -------------------------------------------------------------------------
*/

static struct sockaddr_in sockin;
static int addr_size = sizeof(sockin);

typedef struct serv_s SERV;
typedef struct conn_s CONN;

struct conn_s {
    int fd;                     /* File descriptor for input and output. */
    FILE     * fp;              /* file pointer */
    char     * buf;             /* Buffer for network output. */
    int        dead;            /* Connection is defunct. */
    CONN     * next;
};

struct serv_s {
    int  s_socket;
    int  c_socket;
    char c_addr[20];
};

struct lookup_s {
    int    type;
    char   buf[BIGBUF];
    Bool   status;
};

jmp_buf  start_env;

int    req_error;

int    activity;

CONN * connections;
SERV   serv;

/*
// -------------------------------------------------------------------------
// prototypes
// -------------------------------------------------------------------------
*/

void flush_defunct(void);
void connection_discard(CONN * conn);
void uninit(void);
void catch_signal(int signal);
void init(int port, int * fd);
void flush_defunct(void);
int  my_io_event_wait(long sec);
CONN * connection_add(int fd);
void cwrite(CONN * conn, char * buf);

/*
// -------------------------------------------------------------------------
// functions
// -------------------------------------------------------------------------
*/

void write_err(char *fmt, ...) {
    va_list arg;

    va_start(arg, fmt);
    vfprintf(stderr, fmt, arg);
    va_end(arg);
}

/* ---------------------------------- */
/* what should I do with signals? */
void catch_signal(int signal) {
    switch (signal) {
        case SIGINT:
        case SIGQUIT:
        case SIGTERM:
            fprintf(stderr, "caught signal %d, cleaning up ...\n", signal);
            uninit();
            exit(0);
            break;
        case SIGHUP: {
            CONN * conn;

            fprintf(stderr, "caught SIGHUP, booting connections...\n");

            for (conn = connections; conn; conn = conn->next)
                conn->dead = 1;

            flush_defunct();

            longjmp(start_env, 0);

            break;
        }
    }
}

void init(int port, int * fd) {
    int one=1;

    /* ------------------------------------------------- */
    /* setup signal handling */
    signal(SIGPIPE, SIG_IGN);
    signal(SIGINT,  catch_signal);
    signal(SIGQUIT, catch_signal);
    signal(SIGHUP,  catch_signal);
    signal(SIGTERM, catch_signal);

    /* ------------------------------------------------- */
    /* Create a socket. */
    if ((*fd = socket(AF_INET, SOCK_STREAM, 0)) == F_FAILURE) {
        fprintf(stderr, "init(): socket(): %s\n", strerror(errno));
        exit(errno);
    }

    /* Set SO_REUSEADDR option to avoid restart problems. */
    setsockopt(*fd, SOL_SOCKET, SO_REUSEADDR, (char *) &one, sizeof(int));

    /* Bind the socket to port. */
    memset(&sockin, 0, sizeof(sockin));
    sockin.sin_family = AF_INET;
    sockin.sin_port = htons(port);

    if (bind(*fd, (struct sockaddr *) &sockin, sizeof(sockin)) == F_FAILURE) {
        fprintf(stderr, "init(): bind(): %s\n", strerror(errno));
        exit(errno);
    }

    /*
    // Start listening on port.
    // This should not return an error under any circumstances.
    //
    // (i.e. the failures of listen() will not occur because we are using
    //  it correctly)
    */
    listen(*fd, CONN_QUEUE);
    fprintf(stderr, "listening to port %d\n", port);
}

void flush_defunct(void) {
    CONN **connp, *conn;

    connp = &connections;
    while (*connp) {
        conn = *connp;
        if (conn->dead) {
            *connp = conn->next;
            connection_discard(conn);
        } else {  
            connp = &conn->next;
        }
    }
}

int handle_request (char * req, CONN * conn) {
    char    dnsbuf[DNS_MAXLEN+1],
            code,
          * end,
          * id;

    while (*req && *req == ' ')
        req++;
    if (strlen(req) == 0) {
        req_error = 1;
        return F_FAILURE;
    }

    /* trim the end */
    end = &req[strlen(req) - 1];
    while (end > req && (*end == ' ' || *end == '\n' || *end == '\r'))
        end--;
    *(end+1) = (char) NULL;

    /* get the id */
    id = req;
    while (*req && isdigit(*req))
        req++;
    if (*req != ':') {
        req_error = 2;
        return F_FAILURE;
    }

    *req = (char) NULL;
    req++;
    code = *req;
    if (!code) {
        req_error = 3;
        return F_FAILURE;
    }
    req++;
    if (*req != ':') {
        req_error = 4;
        return F_FAILURE;
    }
    *req = (char) NULL;
    req++;

    switch (code) {
        case 'N': case 'n':
            switch (lookup_name_by_ip(req, dnsbuf)) {
                case DNS_INVADDR:
                    cwrite(conn, id);
                    cwrite(conn, ":N:Invalid IP Address: ");
                    cwrite(conn, req);
                    cwrite(conn, "\n");
                    break;
                case DNS_NORESOLV:
                    cwrite(conn, id);
                    cwrite(conn, ":F:No name for IP Address ");
                    cwrite(conn, req);
                    cwrite(conn, "\n");
                    break;
                case DNS_OVERFLOW:
                    cwrite(conn, id);
                    cwrite(conn, ":F:DNS Response overflows DNS_MAXLEN!\n");
                    break;
                default:
                    cwrite(conn, id);
                    cwrite(conn, ":G:");
                    cwrite(conn, dnsbuf);
                    cwrite(conn, "\n");
                    break;
            }
            break;
        case 'R': case 'r':
            switch (lookup_ip_by_name(req, dnsbuf)) {
                case DNS_NORESOLV:
                    cwrite(conn, id);
                    cwrite(conn, ":F:No name for IP Address ");
                    cwrite(conn, req);
                    cwrite(conn, "\n");
                    break;
                case DNS_OVERFLOW:
                    cwrite(conn, id);
                    cwrite(conn, ":F:DNS Response overflows DNS_MAXLEN!\n");
                    break;
                default:
                    cwrite(conn, id);
                    cwrite(conn, ":G:");
                    cwrite(conn, dnsbuf);
                    cwrite(conn, "\n");
                    break;
            }
            break;
        default:
            cwrite(conn, id);
            cwrite(conn, ":F:Unknown Request Type: ");
            write(conn->fd, &code, 1);
            cwrite(conn, "\n");
    }

    return F_SUCCESS;
}

void handle_io(void) {
    char * p;
    CONN * conn;
    char   linebuf[BIGBUF+1];
    FILE * fp;

    /* read from the sockets now ... */
    for (conn = connections; conn; conn = conn->next) {
        fflush(stdout);
        fp = conn->fp;

        /* read a line */
        if (feof(fp)) {
            conn->dead = 1;
            continue;
        }

        if (!(p = fgets(linebuf, BIGBUF, fp))) {
            if (errno != EAGAIN) {
                fprintf(stderr, "fgets(): %s\n", strerror(errno));
                conn->dead = 1;
            }
            errno = 0;
            continue;
        }

        activity++;

        if (handle_request(p, conn) == F_FAILURE)
            fprintf(stderr, "Invalid Request Format, Error Number %d\n",
                             req_error);
    }
}

int main (void) {
    serv.s_socket = -1;

    /* initialize the server */
    init(NET_PORT, &serv.s_socket);

    setjmp(start_env);

    for (;;) {
        /* wait for new, flush defunct and blast em */
        my_io_event_wait(WAIT_FOR_CONN);
        flush_defunct();

        /* get input, if there is none, sleep a second and try again */
        /* we need the sleep, could load the machine without it */
        /* need to rewrite io_event_wait() to better use select instead */
        activity=0;
        handle_io();
        if (!activity)
            sleep(1);
    }

    uninit();

    return 0;
}

void connection_discard(CONN * conn) {

    if (conn == NULL)
        return;

    fclose(conn->fp);
    close(conn->fd);
    FREE(conn);
}

void cwrite(CONN * conn, char * buf) {
   if (write(conn->fd, buf, strlen(buf)) == F_FAILURE) {
       fprintf(stderr, "write(): %s\n", strerror(errno));
       conn->dead = 1;
   }
}

CONN * connection_add(int fd) {
    CONN * conn;
    int    tmp;

    /* add it to the list of connections */
    conn = MALLOC(CONN, 1);
    conn->fd = fd;
    conn->fp = fdopen(fd, "ab+");
    if (!conn->fp)
        fprintf(stderr, "fdopen(): %s\n", strerror(errno));
    conn->dead = 0; 
    conn->next = connections;
    connections = conn;

    /* set the socket as non-blocking */
    tmp = fcntl(fd, F_GETFL);
    tmp |= O_NONBLOCK;
    fcntl(fd, F_SETFL, tmp);
            
    return conn;
}

void uninit(void) {
    CONN **connp, *conn;

    connp = &connections;
    while (*connp) {
        conn = *connp;
        *connp = conn->next;
        connection_discard(conn);
    }
}

int my_io_event_wait(long sec) {
    struct timeval   tv,
                   * tvp;
    CONN           * conn;
    fd_set           read_fds,
                     write_fds;
    int              nfds,
                     count;

    if (sec == -1) {
        fprintf(stderr, "io_event_wait(): forever wait\n");
        exit(1);
    }

    tv.tv_sec = sec;
    tv.tv_usec = 0;
    tvp = &tv;

    /* Begin with blank file descriptor masks and an nfds of 0. */
    FD_ZERO(&read_fds);
    FD_ZERO(&write_fds);
    nfds = 0;

    /* current connections... */
    for (conn = connections; conn; conn = conn->next) {
        if (!conn->dead)
            FD_SET(conn->fd, &write_fds);
	if (conn->fd >= nfds)
	    nfds = conn->fd + 1;
    }

    FD_SET(serv.s_socket, &read_fds);
    if (serv.s_socket >= nfds)
        nfds = serv.s_socket + 1;

    /* Call select(). */
    count = select(nfds, &read_fds, &write_fds, NULL, tvp);

    /* Lose horribly if select() fails on anything but an
       interrupted system call.  On EINTR, we'll return 0. */
    if (count == F_FAILURE && errno != EINTR) {
	fprintf(stderr, "io_event_wait(): select(): %s\n", strerror(errno));
        exit(1);
    }

    /* no I/O events occurred */
    if (count <= 0)
	return F_FAILURE;

    /* see if anything died on us .. */
    for (conn = connections; conn; conn = conn->next) {
        if (!FD_ISSET(conn->fd, &write_fds))
            conn->dead = 1;
    }

    /* new connections? */
    if (FD_ISSET(serv.s_socket, &read_fds)) {
        serv.c_socket = accept(serv.s_socket,
                                (struct sockaddr *) &sockin,
                                &addr_size);
        if (serv.c_socket >= 0) {
            strcpy(serv.c_addr, inet_ntoa(sockin.sin_addr));

            conn = connection_add(serv.c_socket);
            fprintf(stderr, "Connection from %s\n", serv.c_addr);

            serv.c_socket = -1;
            serv.c_addr[0] = (char) NULL;

        }
    }

    /* at least one I/O event occurred */
    return F_SUCCESS;
}