/* // 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; }