/*
* addr_server.c -- socket-based ip address server.
* 8-92 : Dwayne Fontenot : original coding
*/
#ifdef NeXT
#include <libc.h>
#endif /* NeXT */
#ifndef LATTICE
#include <sys/ioctl.h>
#endif
#include <sys/types.h>
#ifdef __386BSD__
#include <sys/param.h>
#endif /* __386BSD__ */
#if !defined(NeXT) && !defined(hpux) && !defined(apollo) && !defined(LATTICE)
#include <unistd.h>
#endif /* NeXT */
#include <fcntl.h>
#include <sys/time.h>
#ifndef LATTICE
#include <sys/socket.h>
#if !defined(apollo) && !defined(linux)
#include <sys/socketvar.h>
#endif /* !apollo && !linux */
#endif /* LATTICE */
#ifdef _AIX
#include <sys/select.h>
#endif /* _AIX */
#ifndef LATTICE
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#endif
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <ctype.h>
#include <signal.h>
#ifndef LATTICE
#include <memory.h>
#else
#include "amiga.h"
#endif
#include "debug.h"
#include "addr_server.h"
#include "config.h"
#ifdef SunOS_5
#include <stdlib.h>
#endif
#include "lint.h"
#ifdef DEBUG_MACRO
int debug_level = 512;
#endif /* DEBUG_MACRO */
/*
* private local variables.
*/
static connection all_conns[MAX_CONNS];
static int total_conns = 0;
static queue_element_ptr queue_head = NULL;
static queue_element_ptr queue_tail = NULL;
static queue_element_ptr stack_head = NULL;
static int queue_length = 0;
static int conn_fd;
fd_set readmask;
int name_by_ip PROT((int, char *));
int ip_by_name PROT((int, char *));
void init_conns()
{
int i;
for(i=0;i<MAX_CONNS;i++){
all_conns[i].fd = -1;
all_conns[i].state = CLOSED;
all_conns[i].sname[0] = '\0';
}
}
/*
* Initialize connection socket.
*/
void init_conn_sock(port_number)
int port_number;
{
struct sockaddr_in sin;
int sin_len;
int optval;
/*
* create socket of proper type.
*/
if((conn_fd = socket(AF_INET,SOCK_STREAM,0)) == -1){
perror("init_conn_sock: socket");
exit(1);
}
/*
* enable local address reuse.
*/
optval = 1;
if(setsockopt(conn_fd,SOL_SOCKET,SO_REUSEADDR,(char *)&optval,
sizeof(optval)) == -1){
perror("init_conn_sock: setsockopt");
exit(2);
}
/*
* fill in socket address information.
*/
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = INADDR_ANY;
sin.sin_port = htons((u_short)port_number);
/*
* bind name to socket.
*/
if(bind(conn_fd,(struct sockaddr *)&sin,sizeof(sin)) == -1){
perror("init_conn_sock: bind");
exit(3);
}
/*
* get socket name.
*/
sin_len = sizeof(sin);
if(getsockname(conn_fd,(struct sockaddr *)&sin,&sin_len) == -1){
perror("init_conn_sock: getsockname");
exit(4);
}
/*
* register signal handler for SIGPIPE.
*/
if(signal(SIGPIPE,sigpipe_handler) == SIGNAL_ERROR){
perror("init_conn_sock: signal SIGPIPE");
exit(5);
}
/*
* set socket non-blocking
*/
if(set_socket_nonblocking(conn_fd, 1) == -1){
perror("init_user_conn: set_socket_nonblocking 1");
exit(8);
}
/*
* listen on socket for connections.
*/
if(listen(conn_fd,SOMAXCONN) == -1){
perror("init_conn_sock: listen");
exit(10);
}
debug(512,("addr_server: listening for connections on port %d\n",
port_number));
}
/*
* SIGPIPE handler -- does very little for now.
*/
void sigpipe_handler()
{
fprintf(stderr,"SIGPIPE received.\n");
}
/*
* I/O handler.
*/
INLINE void process_io(nb)
int nb;
{
int i;
switch(nb){
case -1:
perror("sigio_handler: select");
break;
case 0:
break;
default:
/*
* check for new connection.
*/
if(FD_ISSET(conn_fd,&readmask)){
debug(512,("sigio_handler: NEW_CONN\n"));
enqueue_datapending(conn_fd,NEW_CONN);
}
/*
* check for data pending on established connections.
*/
for(i=0;i<MAX_CONNS;i++){
if(FD_ISSET(all_conns[i].fd,&readmask)){
debug(512,("sigio_handler: CONN\n"));
enqueue_datapending(all_conns[i].fd,CONN);
}
}
break;
}
}
INLINE void process_queue()
{
int i;
for(i=0;queue_head && (i < MAX_EVENTS_TO_PROCESS);i++){
handle_top_event();
dequeue_top_event();
}
}
void enqueue_datapending(fd, fd_type)
int fd;
int fd_type;
{
queue_element_ptr new_queue_element;
pop_queue_element(&new_queue_element);
new_queue_element->event_type = fd_type;
new_queue_element->fd = fd;
new_queue_element->next = NULL;
if(queue_head){
queue_tail->next = new_queue_element;
}
else {
queue_head = new_queue_element;
}
queue_tail = new_queue_element;
}
void dequeue_top_event()
{
queue_element_ptr top_queue_element;
if(queue_head){
top_queue_element = queue_head;
queue_head = queue_head->next;
push_queue_element(top_queue_element);
}
else {
fprintf(stderr,"dequeue_top_event: tried to dequeue from empty queue!\n");
}
}
void pop_queue_element(the_queue_element)
queue_element_ptr *the_queue_element;
{
if((*the_queue_element = stack_head)) stack_head = stack_head->next;
else *the_queue_element = (queue_element_ptr)malloc(sizeof(queue_element));
queue_length++;
}
void push_queue_element(the_queue_element)
queue_element_ptr the_queue_element;
{
the_queue_element->next = stack_head;
stack_head = the_queue_element;
queue_length--;
}
void handle_top_event()
{
switch(queue_head->event_type){
case NEW_CONN:
debug(512,("handle_top_event: NEW_CONN\n"));
new_conn_handler();
break;
case CONN:
debug(512,("handle_top_event: CONN data on fd %d\n",queue_head->fd));
conn_data_handler(queue_head->fd);
break;
default:
fprintf(stderr,"handle_top_event: unknown event type %d\n",
queue_head->event_type);
break;
}
}
/*
* This is the new connection handler. This function is called by the
* event handler when data is pending on the listening socket (conn_fd).
* If space is available, an interactive data structure is initialized and
* the connected is established.
*/
void new_conn_handler()
{
struct sockaddr_in client;
int client_len;
struct hostent *c_hostent;
int new_fd;
int conn_index;
client_len = sizeof(client);
new_fd = accept(conn_fd,(struct sockaddr *)&client,(int *)&client_len);
if(new_fd == -1){
perror("new_conn_handler: accept");
return;
}
if(total_conns >= MAX_CONNS){
char *message = "no available slots -- closing connection.\n";
fprintf(stderr,"new_conn_handler: no available connection slots.\n");
write(new_fd,message,strlen(message));
if(close(new_fd) == -1)
perror("new_conn_handler: close");
return;
}
/* get some information about new connection */
c_hostent = gethostbyaddr((char *)&client.sin_addr.s_addr,
sizeof(client.sin_addr.s_addr),AF_INET);
for(conn_index = 0;conn_index < MAX_CONNS;conn_index++){
if(all_conns[conn_index].state == CLOSED){
debug(512,("new_conn_handler: opening conn index %d\n",conn_index));
/* update global data for new fd */
all_conns[conn_index].fd = new_fd;
all_conns[conn_index].state = OPEN;
all_conns[conn_index].addr = client;
strcpy(all_conns[conn_index].sname,c_hostent->h_name);
total_conns++;
return;
}
}
fprintf(stderr,"new_conn_handler: sanity check failed!\n");
}
#define IN_BUF_SIZE 80
void conn_data_handler(fd)
int fd;
{
int conn_index;
int buf_index;
int num_bytes;
int msgtype;
static char buf[IN_BUF_SIZE];
if((conn_index = index_by_fd(fd)) == -1){
fprintf(stderr,"conn_data_handler: invalid fd.\n");
return;
}
debug(512,("conn_data_handler: read on fd %d\n",fd));
num_bytes = read(fd,buf,IN_BUF_SIZE);
switch(num_bytes){
case -1:
switch(errno){
case EWOULDBLOCK:
debug(512,("conn_data_handler: read on fd %d: Operation would block.\n",
fd));
break;
default:
perror("conn_data_handler: read");
terminate(conn_index);
break;
}
break;
case 0:
if(all_conns[conn_index].state == CLOSED)
fprintf(stderr,"get_user_data: tried to read from closed fd.\n");
terminate(conn_index);
break;
default:
debug(512,("conn_data_handler: read %d bytes on fd %d\n",num_bytes,fd));
buf_index = 0;
while(buf_index < num_bytes){
memcpy((char *)&msgtype,(char *)&buf[buf_index],sizeof(int));
debug(512,("conn_data_handler: message type: %d\n",msgtype));
switch(msgtype){
case NAMEBYIP:
buf_index += name_by_ip(conn_index,&buf[buf_index]);
break;
/* The reverse to the above... */
case IPBYNAME:
buf_index += ip_by_name(conn_index,&buf[buf_index]);
break;
default:
fprintf(stderr,"conn_data_handler: unknown message type %d\n",msgtype);
buf_index++;
break;
}
}
break;
}
}
#define OUT_BUF_SIZE 80
int ip_by_name(conn_index, buf)
int conn_index;
char *buf;
{
struct hostent *hp;
struct in_addr my_in_addr;
static char out_buf[OUT_BUF_SIZE];
hp = gethostbyname(&buf[sizeof(int)]);
if (hp == NULL) {
/* Failed :( */
sprintf(out_buf, "%s %s\n",&buf[sizeof(int)],"0");
debug(512,("%s",out_buf));
write(all_conns[conn_index].fd, out_buf, strlen(out_buf));
} else {
/* Success! */
memcpy(&my_in_addr, hp->h_addr, sizeof(struct in_addr));
sprintf(out_buf, "%s %s\n", &buf[sizeof(int)],
inet_ntoa(my_in_addr));
debug(512,("%s",out_buf));
write(all_conns[conn_index].fd, out_buf, strlen(out_buf));
}
return(sizeof(int) + strlen(&buf[sizeof(int)]) + 1);
} /* ip_by_name() */
int name_by_ip(conn_index, buf)
int conn_index;
char *buf;
{
long addr;
struct hostent *hp;
static char out_buf[OUT_BUF_SIZE];
if((addr = inet_addr(&buf[sizeof(int)])) == -1){
debug(512,("name_by_ip: malformed address request.\n"));
return(sizeof(int) + strlen(&buf[sizeof(int)]) + 1);
}
if((hp = gethostbyaddr((char *)&addr,sizeof(addr),AF_INET))){
sprintf(out_buf,"%s %s\n",&buf[sizeof(int)],hp->h_name);
debug(512,("%s",out_buf));
write(all_conns[conn_index].fd,out_buf,strlen(out_buf));
} else {
sprintf(out_buf,"%s 0\n",&buf[sizeof(int)]);
debug(512,("%s",out_buf));
write(all_conns[conn_index].fd,out_buf,strlen(out_buf));
debug(512,("name_by_ip: unable to resolve address.\n"));
}
return(sizeof(int) + strlen(&buf[sizeof(int)]) + 1);
}
int index_by_fd(fd)
int fd;
{
int i;
for(i=0;i<MAX_CONNS;i++){
if((all_conns[i].state == OPEN) && (all_conns[i].fd == fd)) return(i);
}
return(-1);
}
void terminate(conn_index)
int conn_index;
{
if(conn_index < 0 || conn_index >= MAX_CONNS){
fprintf(stderr,"terminate: conn_index %d out of range.\n",conn_index);
return;
}
if(all_conns[conn_index].state == CLOSED){
fprintf(stderr,"terminate: connection %d already closed.\n",conn_index);
return;
}
debug(512,("terminating connection %d\n",conn_index));
if(close(all_conns[conn_index].fd) == -1){
perror("terminate: close");
return;
}
all_conns[conn_index].state = CLOSED;
total_conns--;
}
int main(argc,argv)
int argc;
char *argv[];
{
int addr_server_port;
struct timeval timeout;
int i;
int nb;
if(argc > 1){
if((addr_server_port = atoi(argv[1])) == 0){
fprintf(stderr,"addr_server: malformed port number.\n");
exit(2);
}
}
else {
fprintf(stderr,"addr_server: first arg must be port number.\n");
exit(1);
}
init_conn_sock(addr_server_port);
while(1){
/*
* use finite timeout for robustness.
*/
timeout.tv_sec = 2;
timeout.tv_usec = 0;
/*
* clear selectmasks.
*/
FD_ZERO(&readmask);
/*
* set new connection accept fd in readmask.
*/
FD_SET(conn_fd,&readmask);
/*
* set active fds in readmask.
*/
for(i=0;i<MAX_CONNS;i++){
if(all_conns[i].state == OPEN)
FD_SET(all_conns[i].fd,&readmask);
}
#ifndef hpux
nb = select(FD_SETSIZE, &readmask, (fd_set *)0, (fd_set *)0, &timeout);
#else
nb = select(FD_SETSIZE, (int *)&readmask, (int *)0, (int *)0, &timeout);
#endif
if(nb != 0)
process_io(nb);
process_queue();
}
}