#include <lib.h> #include <news.h> #include <save.h> #include <daemons.h> #include <commands.h> #include <message_class.h> #include <talk_type.h> #include <secrets.h> #include <socket.h> #include NETWORK_H inherit LIB_DAEMON; mapping InstData = ([]); mapping sockets = ([]); static string SaveFile; string Name, Myname; static int verbose = 0; static private int icp_socket; varargs static void yenta(string arg, string clr){ if(verbose){ debug_message(arg); } unguarded( (: log_file("/secure/log/icp", timestamp() + " " + strip_colours($(arg)) + "\n") :) ); } varargs static int validate(int i, int soft){ if(!undefinedp(i)){ if(!socket_status(i) || !socket_status(i)[5]){ yenta(mud_name()+" stack: "+get_stack()); yenta("%B_BLACK%^BAD SOCKET ALERT. fd "+i+": "+ identify(socket_status(i))+"\n","red"); if(!soft) error("Bad socket, fd "+i); return 0; } } if(previous_object() != this_object() && !((int)master()->valid_apply(({ "ASSIST" }))) ){ yenta("SECURITY ALERT: validation failure in INSTANCES_D.\n","red"); if(soft) return 0; error("Illegal attempt to access router socket daemon: "+get_stack()+ " "+identify(previous_object(-1))); } return 1; } static void create() { daemon::create(); Myname = "global"; if(!sizeof(InstData)) InstData = ([]); if(!sizeof(sockets)) sockets = ([]); SaveFile = save_file(SAVE_INSTANCES); SetSaveFile(SaveFile); if(!file_exists(SaveFile) && file_exists(old_savename(SaveFile))){ cp(old_savename(SaveFile), SaveFile); } SetNoClean(1); if(file_exists(SaveFile)){ RestoreObject(SaveFile); } if(ENABLE_INSTANCES){ } call_out("Setup", 0); set_heart_beat(10); } string GetMyInstanceName(){ return Myname; } mixed InstCreate(string name, string addy, int port){ object prev = previous_object(); string newglobal, newconf, pfile = base_name(this_player()); if(base_name(prev) != CMD_INSTCONFIG) return 0; if(ENABLE_INSTANCES){ return "Only the global instance can create instances."; } if(InstData[name]) return name + " already exists."; foreach(mixed key, mixed val in InstData){ if(val && val["port"] == port){ return key + " is already on port "+port; } } InstData[name] = ([ "addy" : addy, "port" : port ]); SaveObject(SaveFile, 1); cp(NETWORK_H, "/secure/include/network."+port+".h"); cp(ROOMS_H, "/secure/include/rooms."+port+".h"); cp(SECRETS_H, "/secure/include/secrets."+port+".h"); catch( cp(SAVE_IMC2+".o", SAVE_IMC2 + "." + port + ".o") ); catch( cp(SAVE_MAP+".o", SAVE_MAP + "." + port + ".o") ); catch( cp(SAVE_ROOMS+".o", SAVE_ROOMS + "." + port + ".o") ); catch( cp(SAVE_RACES+".o", SAVE_RACES + "." + port + ".o") ); catch( cp(SAVE_CLASSES+".o", SAVE_CLASSES + "." + port + ".o") ); catch( cp(NEWS_WELCOME, NEWS_WELCOME + "." +port) ); catch( cp(pfile+".o", pfile+"."+port+".o") ); newglobal = read_file("/secure/cfg/global_template.cfg"); if(newglobal){ newglobal = replace_string(newglobal, "TEMPLATE_CONFIG", "/secure/include/config."+port+".h"); newglobal = replace_string(newglobal, "TEMPLATE_NETWORK", "/secure/include/network."+port+".h"); newglobal = replace_string(newglobal, "TEMPLATE_ROOMS", "/secure/include/rooms."+port+".h"); newglobal = replace_string(newglobal, "TEMPLATE_SECRETS", "/secure/include/secrets."+port+".h"); } write_file("/secure/include/global."+port+".h", newglobal, 1); newconf = read_file(CONFIG_H); if(newconf){ string *confarr = explode(newconf, "\n"); newconf = ""; foreach(string line in confarr){ if(!strsrch(line, "#define DISABLE_INTERMUD")){ line = "#define DISABLE_INTERMUD 1"; } if(!strsrch(line, "#define DISABLE_IMC2")){ line = "#define DISABLE_IMC2 1"; } if(!strsrch(line, "#define ENABLE_INSTANCES")){ line = "#define ENABLE_INSTANCES 1"; } if(!strsrch(line, "#define LOG_REMOTE_CHANS")){ line = "#define LOG_REMOTE_CHANS 0"; } if(!strsrch(line, "#define LOG_LOCAL_CHANS")){ line = "#define LOG_LOCAL_CHANS 0"; } newconf += line + "\n"; } write_file("/secure/include/config."+port+".h", newconf, 1); } if(true()){ string newcfg = read_file("/secure/cfg/instance_template.cfg"); string oldcfg = read_file("/secure/cfg/mudos.cfg"); if(newcfg && oldcfg){ string mlib, mbin; sscanf(oldcfg, "%*smudlib directory :%s\n", mlib); sscanf(oldcfg, "%*sbinary directory :%s\n", mbin); newcfg = replace_string(newcfg, "TEMPLATE_NAME", name); newcfg = replace_string(newcfg, "TEMPLATE_PORT", itoa(port)); if(mlib) mlib = trim(mlib); if(mbin) mbin = trim(mbin); newcfg = replace_string(newcfg, "TEMPLATE_LIB", mlib); newcfg = replace_string(newcfg, "TEMPLATE_BIN", mbin); newcfg = replace_string(newcfg, "TEMPLATE_GLOBAL", "global."+port+".h"); write_file("/secure/cfg/mudos."+port+".cfg", newcfg, 1); write_file("/secure/cfg/mudos."+port+".win32", newcfg, 1); } newcfg = read_file("/secure/cfg/runmud_template.bat"); if(newcfg && query_windows()){ newcfg = replace_string(newcfg, "TEMPLATE_MUDOS", "mudos."+port+".win32"); write_file("/secure/cfg/runmud_"+port+".bat", newcfg, 1); } } SaveObject(SaveFile, 1); return 1; } mixed InstDelete(mixed arg){ object prev = previous_object(); mixed which; if(base_name(prev) != CMD_INSTCONFIG) return 0; if(ENABLE_INSTANCES){ return "Only the global instance can delete instances."; } if(InstData[arg]) which = arg; else { if(stringp(arg)) which = atoi(arg); else if(intp(arg)) which = arg; foreach(mixed key, mixed val in InstData){ if(val && val["port"] == which){ which = key; break; } } } if(which && InstData[which]){ mixed port = itoa(InstData[which]["port"]); rm("/secure/include/config."+port+".h"); rm("/secure/include/global."+port+".h"); rm("/secure/include/rooms."+port+".h"); rm("/secure/include/network."+port+".h"); rm("/secure/include/secrets."+port+".h"); rm("/secure/cfg/mudos."+port+".cfg"); rm(NEWS_WELCOME + "." + port); this_object()->ProcessClose(which); InstData[which] = 0; SaveObject(SaveFile, 1); return 1; } else return "No such instance exists."; } string array GetInstances(){ return filter(keys(InstData), (: $1 :)); } mapping GetInstData(){ mapping ret = ([]); foreach(mixed key, mixed val in InstData){ if(key) ret[key] = copy(val); } return ret; } static void SendData(mixed fd, mixed data){ int array targets = ({}); if(stringp(fd) && !undefinedp(InstData[fd])) fd = InstData[fd]["fd"]; if(fd == -2) return; if(fd == -1){ foreach(string name in keys(InstData)){ if(name && InstData[name]) targets += ({ InstData[name]["fd"] }); } } else targets = ({ fd }); foreach(int target in targets){ this_object()->write_data(target, data); } } varargs void SendWhoUpdate(string name, int status){ mapping data = ([ "status" : status ]); object ob = find_player(name); if(ob && !ob->GetInvis()){ string title = ob->GetShort(); if(sizeof(strip_colours(title)) > 50){ title = capitalize(name)+" the Long-Titled."; } data["title"] = title; data["level"] = ob->GetLevel(); data["arch"] = archp(ob); data["creator"] = creatorp(ob); data["builder"] = builderp(ob); if(interactive(ob)) data["status"] = 1; else data["status"] = -1; } else data["status"] = 0; SendData(-1, ({ "who-update", 5, Myname, name, 0, 0, data }) ); } void UpdateInvis(int i){ object ob = previous_object(); string name; if(!ob || !interactive(ob)) return; name = ob->GetKeyName(); if(!sizeof(name)) return; call_out("SendWhoUpdate", 0, name); } varargs void SendTell(string who, string msg, string interwho){ object prev, ob = this_player(); string sender, vname; if(interwho){ ob = previous_object(); if(base_name(ob) != SERVICES_D) return; sender = interwho; vname = interwho; } if(!ob || ob->GetForced()) return; if(!sender) sender = ob->GetKeyName(); if(!vname) vname = ob->GetName(); SendData(-1, ({ "tell", 5, Myname, sender, 0, who, vname, msg }) ); } static void ProcessStartup(mixed data, string addy, int port, int fd){ string name = data[2]; InstData[name] = ([]); InstData[name]["addy"] = addy; InstData[name]["port"] = port; InstData[name]["fd"] = fd; InstData[name]["online"] = 1; InstData[name]["users"] = ([]); if(sizeof(data[7])){ foreach(mixed foo in data[7]){ if(stringp(foo)) InstData[name]["users"][foo] = 1; } } if(ENABLE_INSTANCES){ Myname = data[4]; call_out("SendStartup", 1, fd); } } static void ProcessClose(string name){ if(!sizeof(InstData[name])) return; if(InstData[name]["fd"] > -1){ this_object()->close_connection(InstData[name]["fd"]); } InstData[name]["fd"] = -1; InstData[name]["online"] = 0; } static void ProcessWhoUpdate(mixed data){ if(!sizeof(InstData[data[2]])) return; if(!sizeof(InstData[data[2]]["users"])){ InstData[data[2]]["users"] = ([]); } InstData[data[2]]["users"][data[3]] = data[6]; } static void ProcessTell(mixed data){ object who = find_player(data[5]); string ret; if(!who) return; ret = "%^BOLD%^RED%^"+capitalize(data[3]) + " tells you:%^RESET%^ " + data[7]; who->eventPrint(ret, MSG_CONV); who->SetProperty("reply", data[3]); who->SetProperty("reply_time", time()); who->eventTellHist(ret); } static void ProcessShout(mixed data){ Name = capitalize(data[3]); users()->eventHearTalk(this_object(), 0, TALK_WORLD, "shout", data[6][0], data[6][1]); } string GetName(){ return Name; } static void ReceiveICPData(mixed data, string addy, int port, int fd){ string name; if(!arrayp(data)) return; if(sizeof(data) < 7) return; name = data[2]; if(data[0] != "startup-req"){ if( InstData[name] && InstData[name]["online"]){ if(InstData[name]["addy"] != addy) return; if(InstData[name]["port"] != port ) return; if(!undefinedp(InstData[name]["fd"]) && InstData[name]["fd"] != fd) return; } else{ return; } } switch(data[0]){ case "startup-req" : if(data[6] != INSTANCE_PW) return; else ProcessStartup(data, addy, port, fd); break; case "channel-p" : CHAT_D->eventSendChannel(data[6]...); break; case "who-update": ProcessWhoUpdate(data); break; case "tell": ProcessTell(data); break; case "shout": ProcessShout(data); break; case "close-req" : ProcessClose(name); break; default : break; } if(undefinedp(InstData[name]["fd"])) InstData[name]["fd"] = fd; } string *GetRemoteUsers(string inst){ string *ret = ({}); if(!InstData || !InstData[inst] || !InstData[inst]["users"]) return ret; else ret = filter(keys(InstData[inst]["users"]), (: mapp(InstData[$(inst)]["users"][$1]) && InstData[$(inst)]["users"][$1]["status"] > 0 :) ); return ret; } void eventSendChannel(string name, string ch, string msg, int emote, string target, string targmsg){ mixed packet = ({ name, ch, msg, emote, target, targmsg }); if(base_name(previous_object()) != CHAT_D) return; SendData(-1, ({ "channel-p", 5, Myname, 0, 0, 0, packet }) ); } void eventSendShout(string msg, string lang){ string name; mixed packet = ({ msg, lang }); object ob = previous_object(); if(!ob || !interactive(ob) || ob->GetForced()) return; name = ob->GetName(); SendData(-1, ({ "shout", 5, Myname, name, 0, 0, packet }) ); } static void eventWrite(mixed *packet){ SendData(packet[4], packet); } static void close_connection(int fd){ int sockerr; mixed *sockstat = ({}); validate(); if(fd < 0) return; sockstat = socket_status(fd); if(!sockstat || !sizeof(sockstat)) return; if(sockstat[1] == "LISTEN") return; yenta("About to try closing socket: "+fd, "white"); yenta("Pre-closing state: "+sockstat[1],"yellow"); sockerr = socket_close(fd); yenta("closing socket:"+fd,"white"); yenta("closing sockerr:"+sockerr,"white"); yenta("Post-closing state: "+socket_status(fd)[1],"yellow"); yenta("---\n","white"); } static void close_callback(int fd){ yenta("close_callback: fd="+fd+"\n"); if(fd < 0 || !sizeof(socket_status(fd))) return; if(socket_status(fd)[1] == "LISTEN") return; if(socket_status(fd)[1] == "DATA_XFER") return; close_connection(fd); } static void listen_callback(int fd){ mixed fdstat,newfd; validate(); if ((newfd = socket_accept(fd, "read_callback", "write_callback")) < 0) { return; } else { yenta("socket_accepted: "+newfd+ ", "+identify(socket_status(newfd))+"\n","white"); } } static void read_callback(int fd, mixed info){ mixed sstat; string addy; int port; validate(fd); sstat = socket_status(fd); if(sizeof(sstat) > 3){ string a, b, c, d; int p, i; i = sscanf(sstat[4],"%s.%s.%s.%s.%d",a,b,c,d,p); if(i > 4){ port = (p - OFFSET_ICP); addy = a+"."+b+"."+c+"."+d; } } if(bufferp(info)){ yenta("fd "+fd+" is sending me buffer data!"); yenta("As far as I can tell, it is: "+identify(read_buffer(info)),"blue"); } else yenta("%^WHITE%^data from fd "+fd+":\n%^BLUE%^"+identify(info)); ReceiveICPData(info, addy, port, fd); } static void write_callback(int fd){ validate(fd); if(!sockets[fd]) return; if(sockets[fd]["write_status"] == EEALREADY) { this_object()->write_data(fd, sockets[fd]["pending"]); sockets[fd]["pending"] = 0; } else { sockets[fd]["write_status"] = EESUCCESS; } } static void write_data_retry(int fd, mixed data, int counter){ int rc; int maxtry = 20; if(fd < 0) return; validate(fd); if (counter == maxtry) { return; } rc = socket_write(fd, data); if(!sockets) sockets = ([]); if(!sockets[fd]){ sockets[fd]=([]); } sockets[fd]["write_status"] = rc; switch (rc) { case EESUCCESS: break; case EEALREADY: sockets[fd]["pending"] = data; break; case EECALLBACK: break; case EESECURITY: break; case EEFDRANGE: break; case EENOTCONN: break; case EEBADF: break; default: if (counter < maxtry) { if(counter < 2 || counter > maxtry-1) call_out( (: write_data_retry :), 2 , fd, data, counter + 1 ); return; } } } static int write_data(int fd, mixed data){ int ret; if(fd < 0) return; if(!validate(fd, 1)) return 0; write_data_retry(fd, data, 0); return ret; } static void Setup(){ //yenta("icp setup got called"); if ((icp_socket = socket_create(MUD, "read_callback", "close_callback")) < 0){ log_file("/secure/log/icp","setup: Failed to create socket.\n"); return; } if (socket_bind(icp_socket, PORT_ICP) < 0) { socket_close(icp_socket); log_file("/secure/log/icp","setup: Failed to bind socket to port.\n"); return; } if (socket_listen(icp_socket, "listen_callback") < 0) { socket_close(icp_socket); log_file("/secure/log/icp","setup: Failed to listen to socket.\n"); return; } log_file("/secure/log/icp","icp setup ended\n"); } int eventCreateSocket(string host, int port){ int x, ret; x = socket_create(MUD, "read_callback", "close_callback"); if( x < 0 ) { yenta("Error in socket_create(): "+socket_error(x)); return 0; } ret = x; x = socket_bind(x, 0); if( x != EESUCCESS ) { socket_close(ret); log_file("/secure/log/icp", "Error in socket_bind(): "+x); return 0; } x = socket_connect(ret, host + " " + (port + OFFSET_ICP), "read_callback", "close_callback"); if( x != EESUCCESS ) { socket_close(ret); yenta("Error in socket_connect(): "+socket_error(x)); return x; } return ret; } int DoConnect(string ip, int port, string myname, string name){ int sstat; validate(); sstat = eventCreateSocket(ip, port); if( sstat < 0 ) return; if(!sockets) sockets = ([]); sockets[sstat] = ([]); return sstat; } static void InstConnect(string wat){ int ret = DoConnect(InstData[wat]["addy"], InstData[wat]["port"],"global",wat); if(ret > -1){ InstData[wat]["fd"] = ret; call_out("SendStartup", 1, ret); } else{ yenta("InstConnect FAIL", "red"); } } static void SendStartup(int fd){ string name; foreach(mixed key, mixed val in InstData){ if(key && val["fd"] == fd){ name = key; break; } } SendData(fd, ({"startup-req", 5, Myname, 0, name, 0, INSTANCE_PW, local_users()})); foreach(string user in local_users()){ call_out("SendWhoUpdate", 0, user, 1); } } static void CheckConnections(){ mixed socks = socket_names(); mapping conns = ([]); if(ENABLE_INSTANCES){ return; } if(!sizeof(keys(InstData))){ return; } socks = filter(socks, (: $1[5] == this_object() :) ); foreach(mixed arr in socks){ if(sizeof(arr) > 4){ string a, b, c, d; int p, i; i = sscanf(arr[4],"%s.%s.%s.%s.%d",a,b,c,d,p); if(i < 5) continue; foreach(mixed key, mixed val in InstData){ if(key && mapp(val)){ InstData[key]["fd"] = -2; if(val["port"] == (p - OFFSET_ICP) && val["addy"] == a+"."+b+"."+c+"."+d){ InstData[key]["fd"] = arr[0]; conns[key] = copy(InstData[key]); } } } } } foreach(string foo in keys(InstData)){ if(!foo || !InstData[foo]) continue; InstData[foo]["online"] = 0; if(member_array(foo, keys(conns)) == -1 || InstData[foo]["fd"] == -1){ InstConnect(foo); } else InstData[foo]["online"] = 1; } } void heart_beat(){ if(!ENABLE_INSTANCES) CheckConnections(); }