#include NETWORK_H #include #include #include 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;iclean_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); } }