mud/lib/secure/daemon/i3router/irn.h
2020-09-06 05:43:07 -07:00

854 lines
28 KiB
C

#include NETWORK_H
#include <save.h>
#include <daemons.h>
#include <secrets.h>
static string my_name = ROUTER_NAME;
static string my_password = IRN_PASSWORD;
static string *ok_ips = ({});
static string *desynced = ({});
static int irn_reconnect = 0;
static int irn_timeout = 120;
static int irn_maxtry = 32;
static int convert_channel = 1;
static int convert_channel2 = 0;
static int last_check = time() + random(1000);
mapping PingMap = ([]);
#ifndef PRODUCTION_ROUTER
static int irn_enabled = 0;
static int irn_ping_enabled = 0;
static mapping routers = ([
"*i6" : ([ "ip" : "149.152.218.102", "port" : 25, "password" : IRN_PASSWORD1 ]),
"*i5" : ([ "ip" : "204.209.44.3", "port" : 8180, "password" : IRN_PASSWORD2 ]),
]);
#else
static int irn_enabled = 1;
static int irn_ping_enabled = 1;
static mapping routers = ([
//"*wpr" : ([ "ip" : "195.242.99.94", "port" : 8080, "password" : IRN_PASSWORD1 ]),
"*i4" : ([ "ip" : "204.209.44.3", "port" : 8080, "password" : IRN_PASSWORD2 ]),
"*dalet" : ([ "ip" : "97.107.133.86", "port" : 8787, "password" : IRN_PASSWORD3 ])
]);
#endif
static mapping chan_conv = ([
//"*adsr" : ([ "imud_gossip" : "free_speech" ]),
//"*i4" : ([ "free_speech" : "imud_gossip", "imud_gossip" : "free_speech" ]),
//"*yatmim" : ([ "free_speech" : "imud_gossip", "imud_gossip" : "free_speech" ]),
//"*krakatoa" : ([ "free_speech" : "imud_gossip", "imud_gossip" : "free_speech" ]),
//"*stroggili" : ([ "imud_gossip" : "free_speech" ])
]);
mapping irn_connections = ([]);
mapping irn_sockets = ([]);
static void write_data(int fd, mixed data);
static varargs void SendList(mixed data, int fd, string type);
varargs void SendWholeList(int fd,string type);
static void begin_socket_handoff(int i);
void build_ok_ips(){
ok_ips = ({});
if(irn_enabled){
foreach(mixed key, mixed val in routers){
if(key == my_name) continue;
ok_ips += ({ routers[key]["ip"] });
}
ok_ips = singular_array(ok_ips);
}
}
void irn_checkstat(){
int setuptype = 0;
mixed sstat;
if(!irn_enabled || !sizeof(routers)) return;
build_ok_ips();
foreach(mixed key, mixed val in routers){
if(key != my_name && (!irn_connections[key] || irn_connections[key]["fd"] == -1)){
trr("irn_checkstat: hmmm no irn_connections["+key+"]? see: "+identify(irn_connections));
foreach(mixed key2, mixed val2 in mudinfo){
if(val2["router"] == key && !mudinfo[key2]["disconnect_time"]){
trr(key+" is down, removing its mud: "+key2);
this_object()->disconnect_mud(key2, 1);
}
}
}
}
foreach(mixed key, mixed val in irn_sockets){
sstat = socket_status(key);
if(!sstat || sstat[1] != "DATA_XFER"){
trr("IRN checkstat: removing socket record for: "+identify(key));
trr("it is: "+identify(sstat),"red");
map_delete(irn_sockets, key);
}
if(!irn_connections[val["name"]] ||
irn_connections[val["name"]]["fd"] != key){
trr("IRN checkstat: there is a conflicted record in irn_sockets.");
}
}
foreach(mixed key, mixed val in irn_connections){
if(!PingMap) PingMap = ([]);
if(!key ||!sizeof(key)|| !val) continue;
if(irn_ping_enabled && key != my_name &&
(time() - PingMap[key]) > irn_timeout ){
trr("IRN ping timeout for "+key+"!","red");
trr("Last ping was "+time_elapsed(time() - PingMap[key])+" ago.");
if(irn_connections[key] && irn_connections[key]["fd"]){
map_delete(irn_sockets, irn_connections[key]["fd"]);
map_delete(irn_connections, key);
this_object()->irn_setup(0, key);
return;
}
}
if(!irn_sockets[irn_connections[key]["fd"]] ||
irn_sockets[irn_connections[key]["fd"]]["name"] != key ||
(sstat && sstat[1] != "DATA_XFER")){
trr("IRN checkstat: removing socket and connection record for: "+identify(key));
trr("it is: "+identify(sstat),"green");
map_delete(irn_sockets, irn_connections[key]["fd"]);
map_delete(irn_connections,key);
return;
}
}
foreach(mixed key, mixed val in irn_sockets){
int restart;
sstat = socket_status(key);
if(!sstat || sstat[1] != "DATA_XFER"){
trr("IRN checkstat: removing socket record for: "+identify(key));
trr("it is: "+identify(sstat),"blue");
map_delete(irn_sockets, key);
restart = 1;
}
if(sizeof(irn_connections) && irn_connections[irn_sockets[key]["name"]] && irn_connections[irn_sockets[key]["name"]]["fd"] != key){
trr("IRN checkstat: removing conflicted connection and socket "+key);
map_delete(irn_sockets, irn_connections[irn_sockets[key]["name"]]["fd"]);
map_delete(irn_connections, irn_sockets[key]["name"]);
map_delete(irn_sockets, key);
restart = 1;
}
if(restart){
trr("%^B_BLUE%^I be wantin to restart "+val["name"]+".\nrouters: "+identify(routers));
if(sizeof(routers) > 2) this_object()->irn_setup(0, val["name"]);
return;
}
else setuptype = 1;
}
if(!sizeof(irn_connections) || !sizeof(irn_sockets)){
trr("IRN scheduling an IRN socket clear","red");
setuptype = 1;
}
foreach(mixed key, mixed val in irn_connections){
if(!sizeof(key) || !sizeof(val)) map_delete(irn_connections, key);
}
if((sizeof(routers) -1) != sizeof(irn_connections)){
string *stragglers = ({});
trr("expected "+(sizeof(routers) -1)+" connections, "+identify(routers));
trr("got: "+sizeof(irn_connections)+", "+identify(irn_connections));
foreach(string key, mixed val in routers){
if(key != my_name &&
member_array(key, keys(irn_connections)) == -1){
stragglers += ({ key });
}
}
trr("stragglers: "+identify(stragglers));
foreach(string key in stragglers){
trr("reload irn for "+key);
this_object()->irn_setup(0, key);
}
}
if((time() - last_check) > 90000){
call_out("SendWholeList", 0, 0, "mudlist");
call_out("SendWholeList", 1800, 0, "chanlist");
last_check = time() + random(3600);
}
}
void check_desync(){
if(sizeof(desynced)){
desynced = singular_array(desynced);
foreach(string rtr in desynced){
tc(rtr+" desynced");
this_object()->SendListReq(rtr, "mudlist");
desynced -= ({ rtr });
}
}
}
static int GoodPeer(int fd, mixed data){
string ip = explode(socket_address(fd)," ")[0];
if(!irn_enabled) return 0;
if(member_array(ip,ok_ips) == -1){
server_log("irn: bad ip: "+ip);
server_log("ok_ips: "+identify(ok_ips));
return 0;
}
if(!data || !arrayp(data) || member_array(data[2], keys(routers)) == -1){
trr("IRN: unknown peer");
return 0;
}
if(data[4] != my_name){
trr("IRN: they don't know my name. I am "+my_name+". They think I am: "+identify(data[4]));
return 0;
}
if(data[6]["client_password"] != routers[data[2]]["password"]){
trr("IRN: wrong client password");
return 0;
}
if(data[6]["server_password"] != my_password){
trr("IRN: wrong server password");
return 0;
}
if(!irn_connections[data[2]]) irn_connections[data[2]] = ([]);
irn_connections[data[2]]["fd"] = fd;
return 1;
}
varargs static int ValidatePeer(int fd, mixed data, int outbound){
string ip = explode(socket_address(fd)," ")[0];
mixed tmp;
mixed name;
if(!irn_enabled) return 0;
if(member_array(ip,ok_ips) == -1){
server_log("IRN: bad ip: "+ip);
server_log("ok_ips: "+identify(ok_ips));
return 0;
}
if(!arrayp(data) || sizeof(data) < 5){
trr("IRN ValidatePeer: bad packet");
return 0;
}
if(!outbound) name = data[2];
else name = data[4];
if(mapp(name)) name = name["name"];
if(!outbound && !irn_connections[name]){
trr("IRN ValidatePeer: no such connection.");
return 0;
}
if(member_array(name, keys(routers)) == -1){
trr("IRN ValidatePeer: "+identify(name)+" unknown peer");
return 0;
}
if(this_object()->query_connected_fds()[fd]){
trr("IRN ValidatePeer: lol mud");
this_object()->irn_close_callback(fd);
return 0;
}
if(sizeof(tmp = irn_connections[name])){
if(!mapp(tmp)){
trr("IRN ValidatePeer: wtf");
return 0;
}
if(tmp["fd"] != fd){
trr("validation. fd: "+fd+", tmp: "+identify(tmp),"red");
trr("LOLOLOLOLOL","red");
return 0;
}
}
return 1;
}
static string id_mud(int fd){
string *ret = ({});
foreach(mixed element in keys(irn_connections)){
if(irn_connections[element]["fd"] == fd) ret += ({ element });
}
return implode(ret,", ");
}
void irn_clear(){
if(sizeof(irn_connections))
foreach(mixed key, mixed val in irn_connections){
if(!key || !sizeof(key)) continue;
if(!irn_connections[key] || !irn_connections[key]["fd"]) continue;
this_object()->close_connection(irn_connections[key]["fd"]);
irn_connections[key]["connected"] = 0;
}
if(sizeof(irn_sockets))
foreach(mixed key, mixed val in irn_sockets){
if(!key || !sizeof(key)) continue;
this_object()->close_connection(key);
}
irn_connections = ([]);
irn_sockets = ([]);
foreach(string key, mixed val in routers){
if(!key || !sizeof(key)) continue;
if(key == my_name){
continue;
}
}
SaveObject(SAVE_ROUTER);
}
varargs void irn_setup(int clear, string whom){
mapping which = ([]);
if(!PingMap) PingMap = ([]);
server_log("%^RESET%^irn_setup("+clear+(whom ? (", "+whom ):"")+"): "+get_stack());
foreach(mixed key, mixed val in PingMap){
if(key == my_name || member_array(key,keys(routers)) == -1) map_delete(PingMap,key);
}
if(!irn_enabled) return;
if(clear){
if(sizeof(irn_connections))
foreach(mixed key, mixed val in irn_connections){
if(!irn_connections[key] || !irn_connections[key]["fd"]) continue;
irn_connections[key]["connected"] = 0;
this_object()->close_connection(irn_connections[key]["fd"]);
}
if(sizeof(irn_sockets))
foreach(mixed key, mixed val in irn_sockets){
this_object()->close_connection(key);
}
irn_clear();
}
if(whom) which = ([ whom : routers[whom] ]);
else which = routers;
foreach(string key, mixed val in which){
if(key == my_name){
continue;
}
ok_ips += ({ routers[key]["ip"] });
server_log("ok_ips: "+identify(ok_ips));
if(!irn_connections[key]){
foreach(mixed key2, mixed val2 in mudinfo){
if(val2["router"] && val2["router"] == key &&
!mudinfo[key2]["disconnect_time"]){
trr(key+" is down, disconnecting: "+key2,"red");
this_object()->disconnect_mud(key2, 1);
}
}
}
}
foreach(string name in keys(which)){
int tmp_fd, sockstat;
if(name == my_name){
continue;
}
trr("About to try connecting to: "+identify(name));
tmp_fd = socket_create(MUD, "irn_read_callback","irn_close_callback");
if(tmp_fd < 0){
trr("irn: Couldn't create socket. errorcode: "+socket_error(tmp_fd));
return;
}
irn_connections[name] = ([]);
sockstat = socket_bind(tmp_fd, 0);
trr("socket_bind: "+sockstat);
if(sockstat < 0){
trr("irn: Couldn't bind socket. errorcode: "+socket_error(sockstat));
return;
}
irn_connections[name]["fd"] = tmp_fd;
irn_sockets[tmp_fd] = (["name" : name]);
sockstat = socket_connect(irn_connections[name]["fd"], routers[name]["ip"]+" "+
routers[name]["port"], "irn_read_callback", "irn_write_callback");
trr("socket_connect: "+sockstat);
if(sockstat < 0){
trr("irn: Couldn't connect to "+name+", errorcode: "+socket_error(sockstat));
this_object()->close_connection(irn_connections[name]["fd"]);
irn_connections[name]["fd"] = -1;
}
this_object()->Report();
begin_socket_handoff(tmp_fd);
trr("%^B_WHITE%^Initiating eventSendStartup callout for "+tmp_fd);
call_out( "eventSendStartup", 5, tmp_fd);
}
}
varargs void eventSendStartup(int fd){
mixed *targets = ({});
mixed *packet;
if(!irn_enabled) return;
trr("irn: hit eventSendStartup","yellow");
trr("irn: hit eventSendStartup: "+get_stack(),"green");
if(!fd) targets = keys(irn_connections);
else if(irn_sockets[fd] && irn_sockets[fd]["name"]) targets = ({ irn_sockets[fd]["name"] });
else targets = keys(irn_connections);
if(!sizeof(targets)){
call_out( "irn_setup", 5, 1 );
return;
}
this_object()->Report();
foreach(mixed element in targets){
if(irn_connections[element]["connected"]) continue;
irn_connections[element]["connected"] = 1;
packet = ({
"irn-startup-req",
5,
my_name,
"foo",
element,
"bar",
([ "client_password" : my_password, "server_password" : routers[element]["password"] ])
});
if(ValidatePeer(irn_connections[element]["fd"], packet, 1)){
trr("irn: sending statup to "+element+" on fd "+
irn_connections[element]["fd"]+", aka "+identify(connected_muds[fd]));
write_data(irn_connections[element]["fd"], packet);
call_out( "SendWholeList", 10, irn_connections[element]["fd"]);
}
}
}
static void irn_write_callback(int fd){
}
static void irn_close_callback(int fd){
if(!irn_enabled) return;
if(irn_connections[id_mud(fd)]){
irn_connections[id_mud(fd)]["fd"] = -1;
irn_connections[id_mud(fd)]["connected"] = 0;
}
trr("irn_sockets: "+identify(irn_sockets));
trr("map_delete of "+fd);
map_delete(irn_sockets, fd);
trr("irn_sockets: "+identify(irn_sockets));
trr("I'm wanting to close "+id_mud(fd)+" on fd"+fd+" now.");
}
static void irn_read_callback(int fd, mixed data){
int i;
string tmp="";
mapping MudList = ([]);
if(!irn_enabled) return;
if(this_object()->query_mudinfo())
MudList = this_object()->query_mudinfo();
else MudList = ([ "wt" : "f" ]);
if(!data || !sizeof(data)){
trr("irn: no data");
}
else {
if(bufferp(data)){
for(i=0;i<sizeof(data);i++){
tmp += sprintf("%c",data[i]);
}
}
}
switch(data[0]){
case "irn-startup-req" :
trr("IRN got a startup request on fd"+fd);
if(!GoodPeer(fd, data)) return;
trr("IRN startup from "+data[2]+" accepted.");
if(sizeof(irn_connections)){
foreach(mixed key, mixed val in irn_connections){
if(key == data[2] && fd != irn_connections[key]["fd"]){
map_delete(irn_sockets, irn_connections[key]["fd"]);
}
}
}
if(sizeof(irn_sockets))
foreach(mixed key, mixed val in irn_sockets){
trr("IRN: looking at: "+key+" "+identify(val),"cyan");
if(val["name"] == data[2]){
if(fd != key){
map_delete(irn_sockets, key);
}
}
}
if(!irn_connections[data[2]]) irn_connections[data[2]] = ([]);
irn_connections[data[2]]["fd"] = fd;
irn_connections[data[2]]["connected"] = 1;
trr("irn_connections[\""+data[2]+"\"][\"fd\"]: "+irn_connections[data[2]]["fd"]);
irn_sockets[fd] = ([ "name" : data[2] ]);
call_out( "SendWholeList", 5, fd);
trr(data[2]+" has joined IRN on socket "+fd);
PingMap[data[2]] = time();
trr("%^B_GREEN%^STARTUP COMPLETE: irn_connections: "+identify(irn_connections));
trr("%^B_GREEN%^STARTUP COMPLETE: irn_sockets: "+identify(irn_sockets));
this_object()->clean_ghosts();
return;
case "irn-mudlist-altered" :
if(!ValidatePeer(fd, data)) {
trr("irn: peer failed validation.");
this_object()->close_connection(fd);
if(irn_sockets[fd]) map_delete(irn_sockets, fd);
irn_checkstat();
return;
}
PingMap[data[2]] = time();
this_object()->ReceiveList(data[7],"mudlist",data[2]);
break;
case "irn-mudlist-delta" :
if(!ValidatePeer(fd, data)) {
trr("irn: peer failed validation.");
this_object()->close_connection(fd);
if(irn_sockets[fd]) map_delete(irn_sockets, fd);
irn_checkstat();
return;
}
PingMap[data[2]] = time();
this_object()->ReceiveList(data[7],"mudlist",data[2]);
break;
case "irn-chanlist-altered" :
trr("irn: received irn-chanlist-altered");
if(!ValidatePeer(fd, data)) {
trr("irn: peer failed validation.");
this_object()->close_connection(fd);
if(irn_sockets[fd]) map_delete(irn_sockets, fd);
irn_checkstat();
return;
}
PingMap[data[2]] = time();
this_object()->ReceiveList(data[7],"chanlist",data[2]);
break;
case "irn-chanlist-delta" :
trr("irn: got chanlist delta");
if(!ValidatePeer(fd, data)) {
trr("irn: peer failed validation.");
this_object()->close_connection(fd);
if(irn_sockets[fd]) map_delete(irn_sockets, fd);
irn_checkstat();
return;
}
trr("this_object()->ReceiveList("+identify(data[7])+", chanlist, "+identify(data[2]));
PingMap[data[2]] = time();
this_object()->ReceiveList(data[7],"chanlist",data[2]);
break;
case "irn-mudlist-req" :
if(!ValidatePeer(fd, data)) {
trr("irn-data: peer on fd"+fd+" failed validation.");
this_object()->close_connection(fd);
if(irn_sockets[fd]) map_delete(irn_sockets, fd);
irn_checkstat();
return;
}
PingMap[data[2]] = time();
trr("\nresponding to irn-mudlist-req from "+fd+"\n");
this_object()->SendWholeList(fd, "mudlist");
break;
case "irn-data" :
if(!ValidatePeer(fd, data)) {
trr("irn-data: peer on fd"+fd+" failed validation.");
this_object()->close_connection(fd);
if(irn_sockets[fd]) map_delete(irn_sockets, fd);
irn_checkstat();
return;
}
PingMap[data[2]] = time();
if(convert_channel && !strsrch(data[6][0],"chan") && strsrch(data[6][0],"chan-user")){
if(irn_sockets[fd] && irn_sockets[fd]["name"] && chan_conv[irn_sockets[fd]["name"]]){
foreach(string key, string val in chan_conv[irn_sockets[fd]["name"]]){
data[6][6] = replace_string(data[6][6],key,val);
}
}
}
if(!MudList[data[6][2]] || !MudList[data[6][2]]["connect_time"]){
desynced += ({ data[2] });
desynced = singular_array(desynced);
}
trr("irn_read_callback sending read_callback("+
identify(data[6][2])+", "+identify(data[6])+")");
this_object()->read_callback(data[6][2],data[6]);
break;
case "irn-ping" :
if(!ValidatePeer(fd, data)){
trr("irn: "+data[2]+" failed validation.");
this_object()->close_connection(fd);
if(irn_sockets[fd]) map_delete(irn_sockets, fd);
irn_checkstat();
return;
}
PingMap[data[2]] = time();
break;
default :
if(!ValidatePeer(fd, data)) {
trr("irn: Invalid peer: "+identify(socket_status(fd)),"red");
this_object()->close_connection(fd);
if(irn_sockets[fd]) map_delete(irn_sockets, fd);
if(irn_connections[data[2]]) map_delete(irn_connections, data[2]);
return;
}
PingMap[data[2]] = time();
if(arrayp(data[6])) this_object()->read_callback(data[6][2],data[6]);
return;
}
}
static varargs void SendList(mixed data, int fd, string type){
int *targets = ({});
string *cmuds = this_object()->query_connected_muds();
mixed *outbound = ({});
mapping tmp = ([]);
if(!type || !sizeof(type)) type = "irn-mudlist-delta";
if(type == "mudlist") type = "irn-mudlist-delta";
if(type == "chanlist"){
type = "irn-chanlist-delta";
this_object()->clean_chans();
}
if(!irn_enabled) return;
if(!mapp(data)){
trr("irn: Tried to send a non-map. guilty stack: "+get_stack(),"cyan");
return;
}
tmp = copy(data);
if(mapp(tmp)){
foreach(mixed key, mixed val in tmp){
if(!mapp(val)){
trr("irn: Non mapping val. Key: "+key+". stack: "+get_stack(),"yellow");
trr("irn: guilty stack: "+get_stack(),"cyan");
continue;
}
else {
if(type == "mudlist"){
if(mudinfo[key] && mudinfo[key]["router"] &&
mudinfo[key]["router"] != my_name){
trr("Not sending "+key+"."+mudinfo[key]["router"]);
map_delete(data,key);
}
}
if(type == "chanlist"){
true();
}
}
}
}
if(!fd) targets = keys(irn_sockets);
else targets = ({ fd });
foreach(int router in targets){
if(!irn_sockets[router] || !mapp(irn_sockets[router])) continue;
write_data(router, ({
type,
5,
my_name,
0,
irn_sockets[router]["name"],
0,
time(),
data
}) );
}
}
varargs void SendWholeList(int fd, string type){
mapping tmp = ([]);
int i=1;
this_object()->clean_ghosts();
if(!type || !sizeof(type)) type = "mudlist";
if(!irn_enabled) return;
if(!fd) fd = 0;
trr("SendWholeList type: "+type);
switch(type){
case "mudlist" :
tmp = this_object()->query_mudinfo();
break;
case "chanlist" :
tmp = this_object()->query_chaninfo();
break;
default :
trr("irn SendWholeList error: bad type "+type);
return;
}
foreach(mixed key, mixed val in tmp){
if(type == "mudlist") if(mapp(val)){
i++;
call_out( (: SendList :), i, ([ key : val ]), fd, type );
}
if(type == "chanlist"){
if(key == "listening") continue;
foreach(mixed key2, mixed val2 in val){
i++;
call_out( (: SendList :), i, ([ key : ([ key2 : val2 ]) ]), fd, type );
}
}
}
}
static void SendMessage(mixed data){
string routername, cible;
mapping tmpinfo = this_object()->query_mudinfo();
mixed tmpdata;
mixed *packet;
int *but;
trr("irn: received SendMessage call","white");
if(!irn_enabled) return;
if(tmpinfo && sizeof(data) > 4 && sizeof(tmpinfo[data[4]]) &&
cible = tmpinfo[data[4]]["router"]){
if(mapp(tmpdata = this_object()->query_irn_connections()[cible])){
but = ({ tmpdata["fd"] });
}
}
if(!but || !sizeof(but)) but = keys(irn_sockets);
foreach(int router in but){
trr("irn sending "+data[0]+" for "+identify(data[4])+
" to "+identify(irn_sockets[router]["name"]));
tmpdata = copy(data);
if((convert_channel || convert_channel2) && !strsrch(tmpdata[0],"chan") && strsrch(tmpdata[0],"chan-user")){
routername = irn_sockets[router]["name"];
if(member_array(routername, keys(chan_conv)) != -1){
foreach(string key, string val in chan_conv[routername]){
if(convert_channel){
tmpdata[6] = replace_string(tmpdata[6],val,key);
}
if(convert_channel2){
tmpdata[6] = replace_string(tmpdata[6],key,val);
}
}
}
}
packet = ({
"irn-data",
5,
my_name,
0,
irn_sockets[router]["name"],
0,
tmpdata
});
if(ValidatePeer(router, packet, 1)){
write_data(router, packet);
}
}
}
varargs void SendListReq(mixed rtr, string type){
int fd;
if(!irn_enabled) return;
if(!stringp(rtr)) return;
if(!irn_connections[rtr]) return;
if(undefinedp(irn_connections[rtr]["fd"])) return;
if(irn_connections[rtr]["fd"] < 0) return;
fd = irn_connections[rtr]["fd"];
write_data(fd, ({
"irn-mudlist-req",
5,
my_name,
0,
rtr,
0,
}) );
}
string Report(){
string ret = "IRN: connections: ";
if(sizeof(irn_connections))
foreach(mixed key, mixed val in irn_connections){
if(!key) continue;
if(!irn_connections[key]) return;
ret += key+":"+irn_connections[key]["fd"]+" ";
}
ret += "\n";
ret += "IRN: sockets: ";
foreach(mixed key, mixed val in irn_sockets){
int sstat = -1;
string statmess = "DISCONNECTED";
if(irn_connections[val["name"]]){
sstat = irn_connections[val["name"]]["fd"];
if(sstat > -1 && socket_status(key)){
string *netstat = socket_status(key);
statmess = netstat[1]+" ";
statmess += last_string_element(netstat[3],".");
statmess +=":";
statmess += last_string_element(netstat[4],".") + " ";
}
ret += key +":"+val["name"]+":"+statmess+" ";
}
ret += "\n";
}
ret += "ok_ips: "+identify(ok_ips)+"\n";
foreach(mixed key, mixed val in PingMap){
if(key == my_name) continue;
ret += key+" lastping: "+time_elapsed(time()-val)+" ago\n";
}
return ret;
}
static void begin_socket_handoff(int fd){
object rsock = find_object(RSOCKET_D);
if(!irn_enabled) return;
if(!rsock) rsock = load_object(RSOCKET_D);
if(!rsock){
trr("IRN: Socket handoff failed. Couldn't load RSOCKET_D");
return;
}
socket_release(fd, rsock, "complete_socket_handoff");
}
mapping query_irn_sockets(){
validate();
return copy(irn_sockets);
}
mapping query_irn_connections(){
validate();
return copy(irn_connections);
}
int query_irn_enabled(){
if(irn_enabled) return 1;
return 0;
}
int toggle_irn(int x){
validate();
if(x) irn_enabled = 1;
else irn_enabled = 0;
return irn_enabled;
}
varargs mixed irn_ping(mixed target, int code){
if(!intp(target)){
target = irn_connections[target];
if(target && target["fd"]) target = target["fd"];
else {
return 0;
}
}
if(!irn_sockets[target]) return 0;
if(!code) code = time();
write_data(target, ({
"irn-ping",
5,
my_name,
0,
irn_sockets[target]["name"],
0,
time(),
code
}) );
return 1;
}
int send_pings(){
foreach(string node in keys(routers)){
if(node == my_name) continue;
irn_ping(node);
}
}