#define tell(x) if (find_living("inspiral")) tell_object(find_living("inspiral"), x + "\n");
/*
* Reads in the nameserver config file and gets all of the
* ip's from it.
*/
#include "socket.h"
#include "/adm/net/inet.h"
#define SERVER_PORT 7685
#define TIMEOUT (5*60)
#define CONFIG "/adm/net/config/"
#define ME machines["tabor"]
#define JUST_ADDR(x) sscanf(x, "%s %d", x, womble)
#define NAME 0
#define REVERSE 1
#define LAST_REQ 2
#define SERVICE 3
mapping machines,
reverse,
services,
failed,
requests,
service_req,
sent,
my_write,
services;
string *nameservers,
*pending;
int my_port, womble;
int my_socket, open_server, current_ns;
void load_mappings(string str, string gf);
void setup_nameserver(int port);
void query_nameserver();
void close_callback(int fd);
void write_callback();
void write_service(int fd);
void create() {
string bing;
nameservers = ({ });
machines = ([ ]);
services = ([ "default" : ([ ]) ]);
failed = ([ ]);
reverse = ([ ]);
pending = ({ });
sent = ([ ]);
requests = ([ ]);
my_write = ([ ]);
load_mappings(CONFIG+"nameserver", CONFIG+"services");
/* The default... */
setup_nameserver(my_port);
} /* create() */
void load_mappings(string filen, string ser) {
string file, *bits;
string name, address, flags, tmp;
int i, port, de_port;
file = read_file(filen);
if (file) {
/* Need to do it twice me thinks. Other wise odd sized bits would loose. */
file = implode(explode(file, " ") - ({ "" }), " ");
bits = explode(file, "\n");
for (i=0;i<sizeof(bits);i++) {
/* Do we have a comment? */
if (bits[i][0] == '#' || !strlen(bits[i]))
continue;
/* Ok, addresses are of the form, mudname address flags */
/* flags are NS for nameserver. (All that is named at the moment) */
flags = "";
if (sscanf(bits[i], "%s %s", name, address) == 2) {
port = SERVER_PORT;
if (sscanf(address, "%s:%d%s", address, port, flags) != 3)
sscanf(address, "%s %s", address, flags);
else
sscanf(flags, " %s", flags);
switch (lower_case(flags)) {
case "ns" :
nameservers += ({ address+" "+port });
break;
}
machines[name] = address+" "+port;
/* Case for multiple muds? frog is lets sleeze it... */
reverse[address] = name;
reverse[address+" "+port] = name;
}
}
}
my_port = SERVER_PORT;
sscanf(ME, "%s %d", file, my_port);
file = read_file(ser);
if (file) {
file = implode(explode(file, " ") - ({ "" }), " ");
file = lower_case(file);
bits = explode(file, "\n");
for (i=0;i<sizeof(bits);i++) {
if (bits[i][0] == '#' || !strlen(bits[i]))
continue;
/* The pattern is, name, port */
if (sscanf(bits[i], "%s %s %s", name, address, tmp)) {
if (name != "default") {
name = machines[name];
de_port = SERVER_PORT;
sscanf(name, "%s %d", file, de_port);
} else
de_port = 0;
/* Can only set up things about known muds... */
if (!name) continue;
if (!services[name])
services[name] = ([ ]);
if (tmp[0] == '-') {
sscanf(tmp[1..100], "%d", port);
services[name][address] = de_port+port;
} else if (tmp[0] == '+') {
sscanf(tmp[1..100], "%d", port);
services[name][address] = de_port+port;
} else if (sscanf(tmp, "%d", port) == 1)
if (name == "default")
services[name][address] = ({ port });
else
services[name][address] = port;
}
}
}
} /* load_mappings() */
/*
* Ok, we lookup in our nice array, if we get a hit we returns that name
* if not, we try the first name server. Bit of a problem here though.
*
* Means the return time is not deterministic.
* Oh well. They will have to live with it.
* Keep a failed array so we don't try the same name several million times.
*/
void do_request(int type, string name, string func, mixed args, string *ser) {
if (!ser) ser = ({ });
if (!requests[type])
requests[type] = ([ ]);
if (!requests[type][name]) {
pending += ({ type, name });
if (current_ns == 0)
requests[type][name] = ({ sizeof(nameservers)-1, ser });
else
requests[type][name] = ({ current_ns - 1, ser });
} else
requests[type][name][1] += ser;
requests[type][name] += ({ ({ previous_object(), func, args }) });
query_nameserver();
} /* do_request() */
void service_request(string name, string host, mixed ob, string func,
mixed args) {
int fd, i;
if (!service_req[host]) {
/* We need to open one... */
fd = socket_create(STREAM, "got_service", "close_service");
i = socket_connect(fd, host+" "+SERVER_PORT,
"got_service", "write_service");
if (i < 0) {
JUST_ADDR(host);
call_other(ob, func, name, host, 0, args);
return ;
}
service_req[fd] = host;
service_req[host] = ({ fd, 2, ({ ob, func, name, args }) });
return ;
}
service_req[host] += ({ ({ ob, func, name, args }) });
write_service(service_req[host][0]);
} /* service_request() */
void got_service(int fd, string mess) {
string name, host, str, host2;
int port, i;
mixed *bit;
sscanf(mess, "%s^%d", name, port);
host = service_req[fd];
if (!host) return ;
for (i=2;i<sizeof(service_req[host]);i++)
if (service_req[host][i][2] == name) {
bit = service_req[host][i];
services[host][bit[2]] = port;
host2 = host;
JUST_ADDR(host2);
call_other(bit[0], bit[1], bit[2], host2, port, bit[3]);
service_req[host] = exclude_array(service_req[host], i);
if (service_req[host][1] >= i)
service_req[host][1]--;
i--;
}
if (sizeof(service_req[host]) == 2) {
/* Just the control info... */
map_delete(service_req, host);
map_delete(service_req, fd);
socket_close(fd);
}
} /* got_service() */
void close_service(int fd) {
int i;
string host, host2;
mixed *bit;
/* This means we fluffed it... */
host2 = host = service_req[fd];
if (!host) return ;
JUST_ADDR(host2);
for (i=2;i<sizeof(service_req[host]);i++) {
bit = service_req[host][i];
call_other(bit[0], bit[1], bit[2], host2, 0, bit[3]);
}
map_delete(service_req, host);
map_delete(service_req, fd);
} /* close_service() */
void write_service(int fd) {
string host;
int pos;
host = service_req[fd];
if (!host) {
/* How did this get here? */
socket_close(fd);
return ;
}
/* Anything left to write? */
if ((pos = service_req[host][1]) > sizeof(service_req[host]))
return ;
/* Yep! */
if (socket_write(fd, SERVICE+"^"+service_req[host][pos][2]+"^0") < 0)
return ;
service_req[host][1]++;
} /* write_service() */
void lookup_mud(string name, string finitio, mixed args, string *ser) {
string host;
if (machines[name]) {
host = machines[name];
/*
JUST_ADDR(host);
*/
call_other(previous_object(), finitio, name, host, args);
return ;
}
if (failed[name] || !sizeof(nameservers)) {
call_other(previous_object(), finitio, name, 0, args);
return ;
}
/* Ok, ask any name servers we know about */
do_request(NAME, name, finitio, args, ser);
} /* lookup_mud() */
void reverse_lookup(string address, string func, mixed args, string *ser) {
string *bits;
int i;
bits = keys(machines);
if ((i=member_array(address, bits)) != -1) {
bits = values(machines);
call_other(previous_object(), func, address, bits[i], args);
return ;
}
if (failed[address] || !sizeof(nameservers)) {
call_other(previous_object(), func, address, 0, args);
return ;
}
/* Ok, now we be a nice little frog and setup a nameserver lookup thingy. */
do_request(REVERSE, address, func, args, ser);
} /* reverse_lookup() */
void lookup_service(string name, string host, string func, string args) {
string blue;
if (host[0] < '0' || host[0] > '9') {
/* Need to lookup the host first... */
this_object()->lookup_mud(host, "finish_hostlookup",
({ previous_object(), func, name, args }), ({ }));
return ;
}
if (services["default"][name]) {
string hname;
int port;
port = SERVER_PORT;
sscanf(host, "%s %d", hname, port);
if (pointerp(services["default"][name]))
call_other(previous_object(), func, name, hname,
services["default"][name][0], args);
else
call_other(previous_object(), func, name, hname,
port+services["default"][name], args);
return ;
}
if (!host || host == ME) {
host = ME;
JUST_ADDR(host);
if (!services[ME] || !services[ME][name])
call_other(previous_object(), func, name, host, 0, args);
else
call_other(previous_object(), func, name, host, services[ME][name], args);
return ;
}
/* Irk! Now we need to look the damn thing up :( */
if (services[host][name]) {
blue = host;
JUST_ADDR(blue);
call_other(previous_object(), func, name, blue, services[host][name], args);
return ;
}
tell("Call to service_request");
service_request(name, host, previous_object(), func, args);
} /* lookup_service() */
void finish_hostlookup(string host, string found, mixed *args) {
if (!found) {
call_other(args[0], args[1], args[2], found, 0, args[3]);
return ;
}
if (services["default"][args[2]]) {
string hname;
int port;
port = SERVER_PORT;
sscanf(found, "%s %d", hname, port);
if (pointerp(services["default"][args[2]]))
call_other(args[0], args[1], args[2], hname,
services["default"][args[2]][0], args[3]);
else
call_other(args[0], args[1], args[2], hname,
services["default"][args[2]]+port, args[3]);
return ;
}
if (services[found][args[2]]) {
host = found;
JUST_ADDR(host);
call_other(args[0], args[1], args[2], host, services[found][args[2]],
args[3]);
return ;
}
service_request(args[2], found, args[0], args[1], args[3]);
} /* finish_hostlookup() */
/*
* This will open a connection to a nameserver and ask it, hey! Do you
* know about this name?
*
* This same port is used for service lookups. So we need a nice flag
* We also keep a list of places we have been. To stop loops.
*/
void query_nameserver() {
int i;
if (open_server) {
write_callback();
return ;
}
open_server = socket_create(STREAM, "got_address", "close_callback");
i = socket_connect(open_server, nameservers[current_ns],
"got_address", "write_callback");
if (i < 0)
close_callback(0);
/*
else
write_callback();
*/
} /* query_nameserver() */
void finish_request(int type, string first, mixed ret) {
mixed *bing;
int i;
if (!requests[type])
return ;
if (!requests[type][first])
return ;
bing = requests[type][first];
for (i=2;i<sizeof(bing);i++)
{
catch(call_other(bing[i][0], bing[i][1], first, ret, bing[i][2]));
}
map_delete(requests[type], first);
if (!sizeof(requests[type]))
map_delete(requests, type);
} /* finish_request() */
/*
* This will only get called if our lookup failed. See if we asked
* the last name server, if so. Sulk.
*/
int check_finish(int type, string first) {
if (!requests[type] || !requests[type][first])
return 1; /* Finished */
if (requests[type][first][0] == current_ns) {
finish_request(type, first, 0);
return 1;
}
} /* check_finish() */
void got_address(int fd, mixed message) {
int i;
int type;
string name, res, tmp;
tell("in got_address");
sscanf(message, "%d^%s^%s", type, name, res);
if (sent[type]) {
sent[type] = (mixed *)sent[type] - ({ name });
if (!sizeof(sent[type]))
map_delete(sent, type);
}
switch (type) {
case NAME :
if (res== "0") {
/* Frog! We failed! Sulk. */
/* Sigh.... We failed tell em about the bad news */
if (check_finish(type, name))
failed[type][name] = 1;
break;
}
/* We got an answer! Yes! */
tmp = res;
/* We want more than just an address for this bit. We just want an
* address with services...
JUST_ADDR(tmp);
*/
tell("finsihing request");
finish_request(type, name, tmp);
machines[name] = res;
break;
case REVERSE :
if (res == "0") {
check_finish(type, name);
break;
}
finish_request(type, name, res);
machines[res] = name;
break;
}
/* Wait till we give up on this nameserver and move onto the next */
if (!sizeof(sent) && !sizeof(pending)) {
/* Replyed to all of them */
socket_close(open_server);
/* Does this call the close function? */
close_callback(open_server);
}
} /* got_address() */
void setup_nameserver(int port) {
int i;
my_socket = socket_create(STREAM, "read_callback", "interaction_close");
socket_bind(my_socket, port);
i = socket_listen(my_socket, "connect_callback");
} /* setup_nameserver() */
void write_callback() {
string *path, s;
/*
* Put all of the nameservers in the path as we will ask them all eventually.
*/
if (!sizeof(pending))
return ;
if (requests[pending[0]])
path = requests[pending[0]][1] + nameservers + ({ machines["tabor"] });
else
path = nameservers + ({ machines["tabor"] });
if (socket_write(open_server, (s=(pending[0]+"^"+pending[1]+"^"
+implode(path, "$")))) >= 0) {
if (sent[pending[0]])
sent[pending[0]] += ({ pending[1] });
else
sent[pending[0]] = ({ pending[1] });
pending = pending[2..1000];
}
} /* write_callback() */
void connect_callback(int fd) {
int connected;
if ((connected = socket_accept(fd, "read_callback",
"my_write_callback")) < 0) {
return ;
}
my_write[connected] = 1;
call_out("time_out", TIMEOUT, connected);
} /* connect_callback() */
void time_out(int fd) {
socket_close(fd);
/*
map_delete(my_write, fd);
*/
} /* time_out() */
void my_write_callback(int fd) {
if (!my_write[fd])
my_write[fd] = 1;
if (intp(my_write[fd]))
return ;
if (socket_write(fd, my_write[fd][0]) >= 0)
my_write[fd] = my_write[fd][1..1000];
if (!sizeof(my_write[fd]))
map_delete(my_write, fd);
} /* my_write_callback() */
void interaction_close(int fd) {
INETD->close_service(fd);
map_delete(my_write, fd);
} /* interaction_close() */
void send_back(int fd, string mess) {
if (my_write[fd] && intp(my_write[fd])) {
/* Its ready to be sent to. */
if (socket_write(fd, mess) < 0) {
/* Hmm. bit of the old fail there... */
my_write[fd] = ({ mess });
return ;
}
map_delete(my_write, fd);
return ;
}
my_write[fd] += ({ mess });
} /* send_back() */
void read_callback(int fd, mixed message, mixed blue) {
string *bits;
int i, type;
string name, path;
string *new_p;
if (message[0] >= 'A' && message[0] <= 'Z') {
/* They are talking to our service. Oblige them. */
sscanf(message, "%s^%s", name, path);
INETD->service_contact(fd, name, path);
return ;
}
sscanf(message, "%d^%s^%s", type, name, path);
new_p = explode(path, "$");
switch (type) {
case NAME :
/* Name lookup */
if (machines[name]) {
send_back(fd, type+"^"+name+"^"+machines[name]);
return ;
}
/*
* Ok, we don't know about it. Do we know about any other nameservers
* not in the asked list?
*/
if (sizeof(nameservers - new_p)) {
/* There are some! */
lookup_mud(name, "tell_other_server", ({ fd, type }),
new_p);
} else {
send_back(fd, type+"^"+name+"^"+machines[name]);
}
return ;
case REVERSE :
/* Reverse name lookup */
bits = keys(machines);
if ((i = member_array(name, bits)) != -1) {
bits = values(machines);
send_back(fd, type+"^"+name+"^"+bits[i]);
return ;
}
if (sizeof(nameservers - new_p)) {
reverse_lookup(name, "tell_other_server", ({ fd, type }),
new_p);
} else {
send_back(fd, type+"^"+name+"^0");
}
return ;
case SERVICE :
/* Service lookup */
if (!services[ME])
send_back(fd, type+"^"+name+"^0");
else
send_back(fd, type+"^"+name+"^"+services[ME][name]);
return ;
}
} /* read_callback() */
void tell_other_server(string name, string address, mixed *fd) {
/*
* Don't bother checking if it is still around. Just shove stuff down it.
* it wont complain.
*/
socket_write(fd[0], fd[1]+"^"+name+"^"+address);
} /* tell_other_server() */
void close_callback(int fd) {
int i, j;
mixed *tmp;
/*
* This should be one of our outstanding connections to the rest of
* of the world
*/
current_ns++;
if (current_ns >= sizeof(nameservers))
current_ns = 0;
pending = ({ });
for (i=0;i<LAST_REQ;i++) {
if (!requests[i]) continue;
tmp = keys(requests[i]);
for (j=0;j<sizeof(tmp);j++) {
/* Check for fail */
if (!check_finish(i, tmp[j]) &&
member_array(nameservers[current_ns], requests[i][tmp[j]][1]) == -1)
pending += ({ i, tmp[j] });
}
if (!sizeof(requests[i]))
map_delete(requests, i);
}
if (sizeof(pending)) {
open_server = 0;
query_nameserver();
} else if (sizeof(requests))
/* We still have some unfinished bisness. */
close_callback(open_server);
else
open_server = 0;
} /* close_callback() */
mapping query_services() { return services + ([ ]); }
mapping query_machines() { return machines; }