/* * 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); } nb = select(FD_SETSIZE, &readmask, (fd_set *)0, (fd_set *)0, &timeout); if(nb != 0) process_io(nb); process_queue(); } }