/* Copyright (c) 1993 Stephen F. White */ #include <stdio.h> #ifdef SYSV #include <string.h> #else #include <strings.h> #endif #include <sys/types.h> #include <netinet/in.h> #include <arpa/inet.h> #include <sys/socket.h> #include <sys/time.h> #include <sys/resource.h> #include <sys/wait.h> #include <signal.h> #include <netdb.h> #include "config.h" #include "cool.h" #include "proto.h" #include "sys_proto.h" #include "socket_proto.h" #include "buf.h" #include "netio.h" #include "servers.h" #include "servers_private.h" #ifdef PROTO extern struct hostent *gethostbyname(const char *name); extern struct hostent *gethostbyaddr(GENPTR addr, int len, int type); extern time_t time(time_t *t); extern char *ctime(time_t *t); #endif /* PROTO */ 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, "a"))) { writelog(); perror(configfilename); } else { fprintf(f, "server %s %s %d\n", new->name, new->hostname, new->port); fclose(f); } } #define MATCHES(A, B) (!cool_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, "r"))) { 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 (!cool_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'; } extern int h_errno; Server * serv_add(struct sockaddr_in *from, const char *name) { Server *s, *prev, *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(&(from->sin_addr.s_addr), sizeof(from->sin_addr.s_addr), AF_INET))) { writelog(); fprintf(stderr, "%s", addr_htoa(new->addr)); #ifdef HERROR herror( (char *) 0 ); #else fprintf(stderr, ": gethostbyaddr() failed\n"); #endif 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 (!cool_strcasecmp(s->name, name)) { return s; } } return 0; } int verify_server(Server *s, struct sockaddr_in *from) { struct hostent *h; if (!(h = gethostbyaddr(&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); #ifdef HERROR herror( (char *) 0 ); #else fprintf(stderr, ": gethostbyaddr() failed\n"); #endif return 0; } else if (cool_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; } }