/* Copyright (c) 1993 Stephen F. White */
#include "cool.h"
#include "proto.h"
#include "buf.h"
#include "netio.h"
#include "servers.h"
#include "servers_private.h"
Server *servers = 0;
List *server_list;
static int serverconfig (const char *line, int lineno);
static void serv_addtolist (Serverid id);
static void serv_addtocfg (Server * new);
static char configfilename[MAX_PATH_LEN];
static void serv_addtocfg (Server * new)
{
FILE *f;
if (!(f = fopen (configfilename, "ab"))) {
writelog ();
perror (configfilename);
} else {
fprintf (f, "server %s %s %d\n", new->name, new->hostname, new->port);
fclose (f);
}
}
#define MATCHES(A, B) (!strncasecmp(A, B, strlen(B)))
int read_config (const char *filename)
{
char line[512];
int lineno = 0;
FILE *configfile;
int ok;
strcpy (configfilename, filename);
if (!(configfile = fopen (filename, "rb"))) {
writelog ();
perror (filename);
return -1;
}
servers = 0;
server_list = list_new (0);
while (!feof (configfile)) { /* while there's more input */
if (!fgets (line, sizeof (line), configfile)) { /* get a line */
break;
}
lineno++;
ok = 1;
switch (line[0]) {
case '#':
case '\n':
case '\0':
/* if it starts with '#', or is blank, skip it */
break;
case 'c': /* cache, corefile */
if (MATCHES (line, "cache ")) {
if (cacheconfig (line + 6, lineno)) {
fclose (configfile);
return -3;
}
} else if (MATCHES (line, "corefile")) {
corefile = 1;
} else {
ok = 0;
}
break;
case 's': /* server */
if (MATCHES (line, "server ")) {
if (serverconfig (line + 7, lineno)) {
fclose (configfile);
return -2;
}
} else {
ok = 0;
}
break;
case 'm': /* max_age, max_ticks */
if (MATCHES (line, "max_age ")) {
max_age = atoi (line + 8);
} else if (MATCHES (line, "max_ticks ")) {
max_ticks = atoi (line + 10);
} else {
ok = 0;
}
case 'p': /* player_port, promiscuous */
if (MATCHES (line, "player_port ")) {
player_port = atoi (line + 12);
} else if (MATCHES (line, "promiscuous")) {
promiscuous = 1;
} else {
ok = 0;
}
break;
case 'r': /* registration */
if (MATCHES (line, "registration")) {
registration = 1;
} else {
ok = 0;
}
break;
case 'v': /* verify_servers */
if (MATCHES (line, "verify_servers")) {
verify_servers = 1;
continue;
} else {
ok = 0;
}
break;
default:
ok = 0;
}
/* unknown config command */
if (!ok) {
writelog ();
fprintf (stderr, "Couldn't read config file, line %d\n", lineno);
fclose (configfile);
return -4;
}
}
if (!servers) {
writelog ();
fprintf (stderr, "Must be at least one servers entry in config file\n");
fclose (configfile);
return -5;
}
yo_port = servers->port;
fclose (configfile);
return 0;
}
static int serverconfig (const char *line, int lineno)
{
Server *s;
struct hostent *h;
static Serverid sid = 0;
static Server *prev = 0;
char name[21], hostname[41];
short port;
if (sscanf (line, "%20s %40s %hd", name, hostname, &port) != 3) {
writelog ();
fprintf (stderr, "Bad server entry in config file, line %d\n", lineno);
return -1;
} else if (!(h = gethostbyname (hostname))) {
writelog ();
fprintf (stderr, "Host '%s' not found, line %d\n", hostname, lineno);
return -2;
} else if (h->h_addrtype != AF_INET || h->h_length != 4) {
writelog ();
fprintf (stderr, "Host '%s' not an Internet host, line %d\n",
hostname, lineno);
return -3;
} else {
s = MALLOC (Server, 1);
strcpy (s->name, name);
strcpy (s->hostname, hostname);
s->port = port;
s->addr = ntohl (*((unsigned long *) h->h_addr_list[0]));
s->id = sid++;
s->last_msgid = -1;
s->connected = 0;
s->next = 0;
if (prev) {
prev->next = s;
} else {
servers = s;
}
prev = s;
serv_addtolist (s->id);
}
return 0;
}
const char *serv_id2name (Serverid id)
{
Server *s;
for (s = servers; s; s = s->next) {
if (s->id >= 0 && s->id == id) {
return s->name;
}
}
return "";
}
Serverid serv_name2id (const char *name)
{
Server *s;
for (s = servers; s; s = s->next) {
if (!strcasecmp (s->name, name)) {
return s->id;
}
}
return -1;
}
void serv_id2entry (Serverid server, char *buf)
{
Server *s;
for (s = servers; s; s = s->next) {
if (s->id == server) {
sprintf (buf, "%s %s %d\n", s->name, s->hostname, s->port);
return;
}
}
buf[0] = '\0';
}
Server *serv_add (struct sockaddr_in *from, const char *name)
{
Server *s, *prev = 0, *new;
struct hostent *h;
int newid = 0;
if (serv_addr2server (from) || serv_name2id (name) >= 0) {
return 0;
}
for (s = servers; s; s = s->next) {
prev = s;
if (s->id >= newid) {
newid = s->id + 1;
}
}
new = MALLOC (Server, 1);
new->addr = ntohl (from->sin_addr.s_addr);
new->port = ntohs (from->sin_port);
strncpy (new->name, name, 30);
if (!(h = gethostbyaddr ((void *) &(from->sin_addr.s_addr),
sizeof (from->sin_addr.s_addr), AF_INET))) {
writelog ();
fprintf (stderr, "%s", addr_htoa (new->addr));
fprintf (stderr, ": gethostbyaddr() failed\n");
FREE (new);
return 0;
}
strncpy (new->hostname, h->h_name, 40);
new->last_msgid = -1;
writelog ();
fprintf (stderr, "SERVER %s (%s %d) added successfully.\n",
new->name, new->hostname, new->port);
new->id = newid;
serv_addtolist (newid);
serv_addtocfg (new);
if (prev) {
prev->next = new;
} else {
servers = new;
}
return new;
}
static void serv_addtolist (Serverid id)
{
Var el;
el.type = OBJ;
el.v.obj.id = 0; /* add #0@remoteserver */
el.v.obj.server = id;
server_list = list_setadd (server_list, el);
}
void writelog (void)
{
long t = time ((time_t *) 0);
char *s = ctime ((time_t *) & t);
s[19] = '\0'; /* remove newline */
fprintf (stderr, "%s: ", s + 4);
}
Server *serv_addr2server (struct sockaddr_in *sock)
{
Server *s;
for (s = servers; s; s = s->next) {
if (s->addr == ntohl (sock->sin_addr.s_addr)
&& s->port == ntohs (sock->sin_port)) {
return s;
}
}
return 0;
}
Server *serv_id2server (Serverid server)
{
Server *s;
for (s = servers; s; s = s->next) {
if (s->id == server) {
return s;
}
}
return 0;
}
Server *serv_name2server (const char *name)
{
Server *s;
for (s = servers; s; s = s->next) {
if (!strcasecmp (s->name, name)) {
return s;
}
}
return 0;
}
int verify_server (Server * s, struct sockaddr_in *from)
{
struct hostent *h;
if (!(h = gethostbyaddr ((void *) &from->sin_addr.s_addr,
sizeof (from->sin_addr.s_addr), AF_INET))) {
writelog ();
fprintf (stderr, "Failed verify host %s as %s (%s %d)",
addr_htoa (ntohl (from->sin_addr.s_addr)), s->name, s->hostname,
s->port);
fprintf (stderr, ": gethostbyaddr() failed\n");
return 0;
} else if (strcasecmp (h->h_name, s->hostname)
|| ntohs (from->sin_port) != s->port) {
writelog ();
fprintf (stderr, "Host %s %d tried to connect as server %s (%s %d)\n",
h->h_name, ntohs (from->sin_port), s->name, s->hostname, s->port);
return 0;
} else {
return 1;
}
}
/*
* serv_discardmsg()
*
* Returns non-zero if the system should discard a received message.
*/
int serv_discardmsg (Serverid server, int msgid)
{
Server *s = serv_id2server (server);
if (!s) {
return -1;
}
if (msgid > s->last_msgid) {
s->last_msgid = msgid;
return 0;
} else {
return 1;
}
}