home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Usenet 1994 October
/
usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso
/
games
/
volume11
/
tinymud2
/
part08
/
conc.c
next >
Wrap
C/C++ Source or Header
|
1990-08-10
|
12KB
|
511 lines
/* tinyMUD port concentrator by Robert Hood */
/* Revision 2.0 */
#include <stdio.h>
#include <signal.h>
#include <sys/param.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/time.h>
#include <sys/errno.h>
#include <fcntl.h>
#include <netdb.h>
#include "config.h"
void queue_message(int port, char *data, int len);
void writelog(const char *fmt,...);
#define BUFLEN 65536
#define CONC_MESSAGE "[ Connected to the TinyMUD port concentrator ]\n"
#define PANIC_MESSAGE "\nGoing Down - Bye!\n"
struct message
{
char *data;
short len;
struct message *next;
};
struct conc_list
{
char status;
/*
* Status: 0 = Not connected 1 = Connected 2 = Disconnecting (waiting till
* queue is empty)
*/
struct message *first, *last;
} *clist;
int mud_sock;
int sock;
int pid;
int port = TINYPORT;
int intport = INTERNAL_PORT;
int clvl = 1;
main(argc, argv)
int argc;
char *argv[];
{
int l;
if (argc > 1)
port = atoi(argv[1]);
if (argc > 2)
intport = atoi(argv[2]);
if (argc > 3)
clvl = atoi(argv[3]);
signal(SIGPIPE, SIG_IGN); /* Ignore I/O signals */
for (l = 3; l < NOFILE; ++l) /* Close all files from last process */
close(l); /* except stdin, stdout, stderr */
pid = 1;
connect_mud(); /* Connect to interface.c */
setup(); /* Setup listen port */
mainloop(); /* main loop */
}
connect_mud()
{
int temp;
struct sockaddr_in sin;
mud_sock = 0;
while (mud_sock == 0)
{
mud_sock = socket(AF_INET, SOCK_STREAM, 0);
if (mud_sock < 0)
{
perror("socket");
mud_sock = 0;
} else
{
temp = 1;
setsockopt(mud_sock, SOL_SOCKET, SO_REUSEADDR, (char *)&temp, sizeof(temp));
sin.sin_family = AF_INET;
sin.sin_port = htons(intport);
sin.sin_addr.s_addr = htonl(0x7F000001);
temp = connect(mud_sock, (struct sockaddr *) & sin, sizeof(sin));
if (temp < 0)
{
perror("connect");
close(mud_sock);
mud_sock = 0;
}
}
if (mud_sock == 0)
{
sleep(1);
fputs("retrying....\n", stderr);
}
}
if (fcntl(mud_sock, F_SETFL, FNDELAY) == -1)
{
perror("make_nonblocking: fcntl");
}
if (fcntl(mud_sock, F_SETFD, 1) == -1)
{
perror("close on execve: fcntl");
}
#ifdef DEBUG
fputs("connected!\n", stderr);
#endif
}
setup()
{
int temp;
struct sockaddr_in sin;
sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock < 1)
{
perror("socket");
exit(-1);
}
temp = 1;
setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *)&temp, sizeof(temp));
sin.sin_family = AF_INET;
sin.sin_port = htons(port);
sin.sin_addr.s_addr = htonl(INADDR_ANY);
temp = bind(sock, (struct sockaddr *) & sin, sizeof(sin));
if (temp < 0)
{
perror("bind");
exit(1);
}
temp = listen(sock, 5);
if (temp < 0)
{
perror("listen");
exit(1);
}
}
mainloop()
{
int found, newsock, lastsock, len, loop;
int accepting, current = 0;
int temp;
struct timeval tv;
struct sockaddr_in sin;
struct hostent *hent;
fd_set in, out;
char *data;
char *buf, header[4];
struct conc_list *cptr;
struct message *tmsg;
short templen;
char *mainbuf, *outbuf;
int mainlen, outlen;
int command;
int hlen;
char *hostnm;
/* Allocate huge buffer */
data = (char *)malloc(65536);
/* Allocate array, one for each possible socket */
clist = (struct conc_list *) malloc(sizeof(struct conc_list) * NOFILE);
/* Allocate I/O buffers for main I/O socket */
mainbuf = (char *)malloc(BUFLEN);
mainlen = 0;
outbuf = (char *)malloc(BUFLEN);
outlen = 0;
if (!data || !clist || !mainbuf || !outbuf)
{
perror("malloc");
exit(1);
}
/* Init array */
for (loop = 0; loop < NOFILE; ++loop)
{
cptr = &(clist[loop]);
cptr->status = 0;
cptr->first = 0;
cptr->last = 0;
}
/*
* Accept connections flag ON accepting = 1; /* lastsock for select()
*/
lastsock = sock + 1;
/* mud_sock has already been established */
clist[mud_sock].status = 1;
/* Special port # for control messages */
clist[0].status = 1;
while (1)
{
if (pid < 0)
{
pid = vfork();
}
if (pid == 0)
{
char pstr[32], istr[32], cstr[32];
sprintf(pstr, "%d", port);
sprintf(istr, "%d", intport);
sprintf(cstr, "%d", clvl + 1);
execlp("concentrate", "conc", pstr, istr, cstr, 0);
writelog("CONC %d:ACK!!!!!! exec failed! Exiting...\n", clvl);
exit(1);
/* Gee...now what? Should I try again? */
}
/* zero out port selector masks */
FD_ZERO(&in);
FD_ZERO(&out);
/* set apropriate bit masks for I/O */
if (accepting)
FD_SET(sock, &in);
for (loop = 1; loop < NOFILE; ++loop)
{
cptr = &(clist[loop]);
if (cptr->status)
{
FD_SET(loop, &in);
if (cptr->first)
FD_SET(loop, &out);
}
}
if (outlen > 0)
FD_SET(loop, &out);
/* timeout for select */
tv.tv_sec = 1000;
tv.tv_usec = 0;
/* look for ports waiting for I/O */
found = select(lastsock, &in, &out, (fd_set *) 0, &tv);
/* None found, skip the rest... */
if (found < 0)
continue;
/* New connection? */
if (accepting && FD_ISSET(sock, &in))
{
len = sizeof(sin);
newsock = accept(sock, (struct sockaddr *) & sin, &len);
/* This limits the # of connections per concentrator */
if (newsock >= (NOFILE - 5))
{
close(sock);
accepting = 0;
pid = -1;
}
if (newsock >= lastsock)
lastsock = newsock + 1;
cptr = &(clist[newsock]);
cptr->status = 1;
cptr->first = 0;
cptr->last = 0;
/* set to non-blocking mode */
if (fcntl(newsock, F_SETFL, FNDELAY) == -1)
{
perror("make_nonblocking: fcntl");
}
/* set to close on execv */
if (fcntl(newsock, F_SETFD, 1) == -1)
{
perror("close on execv: fcntl");
}
temp = 1;
if (setsockopt(newsock, SOL_SOCKET, SO_KEEPALIVE, (char *)&temp,
sizeof(temp)) < 0)
{
perror("keepalive setsockopt");
}
queue_message(newsock, CONC_MESSAGE, sizeof(CONC_MESSAGE) - 1);
/* build control code for connect */
data[0] = 0;
data[1] = 1; /* connect */
data[2] = newsock;
bcopy(&(sin.sin_addr.s_addr), data + 3, 4);
hent = gethostbyaddr(&(sin.sin_addr.s_addr),
sizeof(sin.sin_addr.s_addr), AF_INET);
if (hent)
strcpy(data + 7, hent->h_name);
else
strcpy(data + 7, inet_ntoa(sin.sin_addr.s_addr));
queue_message(mud_sock, data, 7 + strlen(data + 7));
#ifdef DEBUG
writelog("CONC %d: USER CONNECT: sock %d, host %s\n", clvl,
newsock, data + 7);
#endif
}
/* recieve data from ports */
for (loop = 0; loop < NOFILE; ++loop)
{
cptr = &(clist[loop]);
if (cptr->status && FD_ISSET(loop, &in))
{
if (loop == 0)
{
} else
if (loop == mud_sock)
{
if (mainlen < BUFLEN)
{
len = recv(loop, mainbuf + mainlen,
BUFLEN - mainlen, 0);
if (len <= 0)
{
/* This is quite useless, but what else am I supposed to do? */
writelog("CONC %d: Lost Connection\n", clvl);
panic();
}
mainlen += len;
}
while (mainlen > 2)
{
bcopy(mainbuf, &templen, 2);
if (mainlen >= (templen + 2))
{
queue_message(*(mainbuf + 2), mainbuf + 3,
templen - 1);
mainlen = mainlen - templen - 2;
bcopy(mainbuf + templen + 2, mainbuf, mainlen);
} else
break;
}
} else
{
/* data + 1 so we can add port later w/o a bcopy */
len = recv(loop, data + 1, 65530, 0);
if (len == 0)
{
disconnect(loop);
} else
if (len < 0)
{
/* Hmm..... */
writelog("CONC %d: recv: %s\n", clvl, strerror(errno));
} else
{
/* Add the port # to the data, and send it to interface.c */
data[0] = loop;
queue_message(mud_sock, data, len + 1);
}
}
}
}
/* Handle output */
for (loop = 0; loop < NOFILE; ++loop)
{
cptr = &(clist[loop]);
if ((loop == 0) && (cptr->first))
{
command = *(cptr->first->data);
switch (command)
{
case 2: /* disconnect */
if (clist[*(cptr->first->data + 1)].status)
clist[*(cptr->first->data + 1)].status = 2;
else
writelog("CONC %d: Recieved dissconnect for unknown user\n", clvl);
break;
default:
writelog("CONC %d: Recieved unknown command %d\n", clvl, command);
break;
}
free(cptr->first->data);
tmsg = cptr->first;
cptr->first = cptr->first->next;
free(tmsg);
if (!cptr->first)
cptr->last = 0;
} else
if ((loop == mud_sock) && FD_ISSET(mud_sock, &out) &&
((cptr->first) || (outlen > 0)))
{
while ((cptr->first) &&
((BUFLEN - outlen) > (cptr->first->len + 2)))
{
templen = cptr->first->len;
bcopy(&(templen), outbuf + outlen, 2);
bcopy(cptr->first->data, outbuf + outlen + 2, templen);
outlen += templen + 2;
free(cptr->first->data);
tmsg = cptr->first;
cptr->first = cptr->first->next;
free(tmsg);
if (!cptr->first)
cptr->last = 0;
}
if (outlen)
{
len = send(mud_sock, outbuf, outlen, 0);
if (len > 0)
{
outlen -= len;
bcopy(outbuf + len, outbuf, outlen);
}
else
{
panic();
}
}
} else
if (FD_ISSET(loop, &out) && (cptr->first))
{
len = send(loop, cptr->first->data, cptr->first->len, 0);
free(cptr->first->data);
tmsg = cptr->first;
cptr->first = cptr->first->next;
free(tmsg);
if (!cptr->first)
cptr->last = 0;
}
/* Test for pending disconnect */
else
if ((cptr->status == 2) && (cptr->first == 0))
{
cptr->status = 0;
shutdown(loop, 0);
close(loop);
}
}
/* Test for emptyness */
if (!accepting)
{
for (loop = mud_sock + 1; loop < NOFILE; ++loop)
if (clist[loop].status)
break;
if (loop == NOFILE)
exit(0);
}
}
}
/* Properly disconnect a user */
disconnect(user)
int user;
{
char header[4];
/* make control message for disconnect */
header[0] = 0;
header[1] = 2; /* disconnect code */
header[2] = user;
queue_message(mud_sock, header, 3);
/* shutdown this socket */
clist[user].status = 0;
close(user);
#ifdef DEBUG
writelog("CONC %d: USER DISCONNECT: %d\n", clvl, user);
#endif
}
void queue_message(int port, char *data, int len)
{
struct message *ptr;
ptr = (struct message *) malloc(sizeof(struct message));
ptr->data = (char *)malloc(len);
ptr->len = len;
bcopy(data, ptr->data, len);
ptr->next = 0;
if (clist[port].last == 0)
clist[port].first = ptr;
else
(clist[port].last)->next = ptr;
clist[port].last = ptr;
}
/* Kill off all connections quickly */
panic()
{
int loop;
for (loop = 1; loop < NOFILE; ++loop)
{
if (clist[loop].status)
{
send(loop, PANIC_MESSAGE, sizeof(PANIC_MESSAGE), 0);
shutdown(loop, 0);
close(loop);
}
}
exit(1);
}
/* Modified to send stuff to the main server for logging */
void writelog(const char *fmt,...)
{
va_list list;
struct tm *tm;
long t;
char buffer[2048];
va_start(list, fmt);
vsprintf(buffer + 2, fmt, list);
buffer[0] = 0;
buffer[1] = 4; /* remote log command */
queue_message(mud_sock, buffer, strlen(buffer + 2) + 2);
va_end(list);
}