ds2.9a12/bin/
ds2.9a12/extra/
ds2.9a12/extra/crat/
ds2.9a12/extra/creremote/
ds2.9a12/extra/mingw/
ds2.9a12/extra/wolfpaw/
ds2.9a12/fluffos-2.14-ds13/
ds2.9a12/fluffos-2.14-ds13/Win32/
ds2.9a12/fluffos-2.14-ds13/compat/
ds2.9a12/fluffos-2.14-ds13/compat/simuls/
ds2.9a12/fluffos-2.14-ds13/include/
ds2.9a12/fluffos-2.14-ds13/testsuite/
ds2.9a12/fluffos-2.14-ds13/testsuite/clone/
ds2.9a12/fluffos-2.14-ds13/testsuite/command/
ds2.9a12/fluffos-2.14-ds13/testsuite/data/
ds2.9a12/fluffos-2.14-ds13/testsuite/etc/
ds2.9a12/fluffos-2.14-ds13/testsuite/include/
ds2.9a12/fluffos-2.14-ds13/testsuite/inherit/
ds2.9a12/fluffos-2.14-ds13/testsuite/inherit/master/
ds2.9a12/fluffos-2.14-ds13/testsuite/log/
ds2.9a12/fluffos-2.14-ds13/testsuite/single/
ds2.9a12/fluffos-2.14-ds13/testsuite/single/tests/compiler/
ds2.9a12/fluffos-2.14-ds13/testsuite/single/tests/efuns/
ds2.9a12/fluffos-2.14-ds13/testsuite/single/tests/operators/
ds2.9a12/fluffos-2.14-ds13/testsuite/u/
ds2.9a12/lib/cmds/admins/
ds2.9a12/lib/cmds/common/
ds2.9a12/lib/cmds/creators/include/
ds2.9a12/lib/daemon/services/
ds2.9a12/lib/daemon/tmp/
ds2.9a12/lib/doc/
ds2.9a12/lib/doc/bguide/
ds2.9a12/lib/doc/efun/all/
ds2.9a12/lib/doc/efun/arrays/
ds2.9a12/lib/doc/efun/buffers/
ds2.9a12/lib/doc/efun/compile/
ds2.9a12/lib/doc/efun/floats/
ds2.9a12/lib/doc/efun/functions/
ds2.9a12/lib/doc/efun/general/
ds2.9a12/lib/doc/efun/mixed/
ds2.9a12/lib/doc/efun/numbers/
ds2.9a12/lib/doc/efun/parsing/
ds2.9a12/lib/doc/hbook/
ds2.9a12/lib/doc/help/classes/
ds2.9a12/lib/doc/help/races/
ds2.9a12/lib/doc/lfun/
ds2.9a12/lib/doc/lfun/all/
ds2.9a12/lib/doc/lfun/lib/abilities/
ds2.9a12/lib/doc/lfun/lib/armor/
ds2.9a12/lib/doc/lfun/lib/bank/
ds2.9a12/lib/doc/lfun/lib/bot/
ds2.9a12/lib/doc/lfun/lib/clay/
ds2.9a12/lib/doc/lfun/lib/clean/
ds2.9a12/lib/doc/lfun/lib/clerk/
ds2.9a12/lib/doc/lfun/lib/client/
ds2.9a12/lib/doc/lfun/lib/combat/
ds2.9a12/lib/doc/lfun/lib/connect/
ds2.9a12/lib/doc/lfun/lib/container/
ds2.9a12/lib/doc/lfun/lib/corpse/
ds2.9a12/lib/doc/lfun/lib/creator/
ds2.9a12/lib/doc/lfun/lib/daemon/
ds2.9a12/lib/doc/lfun/lib/damage/
ds2.9a12/lib/doc/lfun/lib/deterioration/
ds2.9a12/lib/doc/lfun/lib/donate/
ds2.9a12/lib/doc/lfun/lib/door/
ds2.9a12/lib/doc/lfun/lib/equip/
ds2.9a12/lib/doc/lfun/lib/file/
ds2.9a12/lib/doc/lfun/lib/fish/
ds2.9a12/lib/doc/lfun/lib/fishing/
ds2.9a12/lib/doc/lfun/lib/flashlight/
ds2.9a12/lib/doc/lfun/lib/follow/
ds2.9a12/lib/doc/lfun/lib/ftp_client/
ds2.9a12/lib/doc/lfun/lib/ftp_data_connection/
ds2.9a12/lib/doc/lfun/lib/fuel/
ds2.9a12/lib/doc/lfun/lib/furnace/
ds2.9a12/lib/doc/lfun/lib/genetics/
ds2.9a12/lib/doc/lfun/lib/holder/
ds2.9a12/lib/doc/lfun/lib/id/
ds2.9a12/lib/doc/lfun/lib/interactive/
ds2.9a12/lib/doc/lfun/lib/lamp/
ds2.9a12/lib/doc/lfun/lib/leader/
ds2.9a12/lib/doc/lfun/lib/light/
ds2.9a12/lib/doc/lfun/lib/limb/
ds2.9a12/lib/doc/lfun/lib/living/
ds2.9a12/lib/doc/lfun/lib/load/
ds2.9a12/lib/doc/lfun/lib/look/
ds2.9a12/lib/doc/lfun/lib/manipulate/
ds2.9a12/lib/doc/lfun/lib/meal/
ds2.9a12/lib/doc/lfun/lib/messages/
ds2.9a12/lib/doc/lfun/lib/player/
ds2.9a12/lib/doc/lfun/lib/poison/
ds2.9a12/lib/doc/lfun/lib/position/
ds2.9a12/lib/doc/lfun/lib/post_office/
ds2.9a12/lib/doc/lfun/lib/potion/
ds2.9a12/lib/doc/lfun/lib/room/
ds2.9a12/lib/doc/lfun/lib/server/
ds2.9a12/lib/doc/lfun/lib/spell/
ds2.9a12/lib/doc/lfun/lib/torch/
ds2.9a12/lib/doc/lfun/lib/vendor/
ds2.9a12/lib/doc/lfun/lib/virt_sky/
ds2.9a12/lib/doc/lfun/lib/weapon/
ds2.9a12/lib/doc/lfun/lib/worn_storage/
ds2.9a12/lib/doc/lpc/basic/
ds2.9a12/lib/doc/lpc/concepts/
ds2.9a12/lib/doc/lpc/constructs/
ds2.9a12/lib/doc/lpc/etc/
ds2.9a12/lib/doc/lpc/intermediate/
ds2.9a12/lib/doc/lpc/types/
ds2.9a12/lib/doc/misc/
ds2.9a12/lib/doc/old/
ds2.9a12/lib/domains/
ds2.9a12/lib/domains/Praxis/adm/
ds2.9a12/lib/domains/Praxis/attic/
ds2.9a12/lib/domains/Praxis/cemetery/mon/
ds2.9a12/lib/domains/Praxis/data/
ds2.9a12/lib/domains/Praxis/death/
ds2.9a12/lib/domains/Praxis/mountains/
ds2.9a12/lib/domains/Praxis/obj/armour/
ds2.9a12/lib/domains/Praxis/obj/magic/
ds2.9a12/lib/domains/Praxis/obj/weapon/
ds2.9a12/lib/domains/Praxis/orc_valley/
ds2.9a12/lib/domains/Ylsrim/
ds2.9a12/lib/domains/Ylsrim/adm/
ds2.9a12/lib/domains/Ylsrim/armor/
ds2.9a12/lib/domains/Ylsrim/broken/
ds2.9a12/lib/domains/Ylsrim/fish/
ds2.9a12/lib/domains/Ylsrim/meal/
ds2.9a12/lib/domains/Ylsrim/npc/
ds2.9a12/lib/domains/Ylsrim/obj/
ds2.9a12/lib/domains/Ylsrim/virtual/
ds2.9a12/lib/domains/Ylsrim/weapon/
ds2.9a12/lib/domains/campus/adm/
ds2.9a12/lib/domains/campus/etc/
ds2.9a12/lib/domains/campus/meals/
ds2.9a12/lib/domains/campus/save/
ds2.9a12/lib/domains/campus/txt/ai/charles/
ds2.9a12/lib/domains/campus/txt/ai/charles/bak2/
ds2.9a12/lib/domains/campus/txt/ai/charles/bak2/bak1/
ds2.9a12/lib/domains/campus/txt/ai/charly/
ds2.9a12/lib/domains/campus/txt/ai/charly/bak/
ds2.9a12/lib/domains/campus/txt/jenny/
ds2.9a12/lib/domains/cave/doors/
ds2.9a12/lib/domains/cave/etc/
ds2.9a12/lib/domains/cave/meals/
ds2.9a12/lib/domains/cave/weap/
ds2.9a12/lib/domains/default/creator/
ds2.9a12/lib/domains/default/doors/
ds2.9a12/lib/domains/default/etc/
ds2.9a12/lib/domains/default/vehicles/
ds2.9a12/lib/domains/default/virtual/
ds2.9a12/lib/domains/default/weap/
ds2.9a12/lib/domains/town/txt/shame/
ds2.9a12/lib/domains/town/virtual/
ds2.9a12/lib/domains/town/virtual/bottom/
ds2.9a12/lib/domains/town/virtual/space/
ds2.9a12/lib/estates/
ds2.9a12/lib/ftp/
ds2.9a12/lib/lib/comp/
ds2.9a12/lib/lib/daemons/
ds2.9a12/lib/lib/daemons/include/
ds2.9a12/lib/lib/lvs/
ds2.9a12/lib/lib/user/
ds2.9a12/lib/lib/virtual/
ds2.9a12/lib/log/
ds2.9a12/lib/log/adm/
ds2.9a12/lib/log/archive/
ds2.9a12/lib/log/chan/
ds2.9a12/lib/log/errors/
ds2.9a12/lib/log/law/adm/
ds2.9a12/lib/log/law/email/
ds2.9a12/lib/log/law/names/
ds2.9a12/lib/log/law/sites-misc/
ds2.9a12/lib/log/law/sites-register/
ds2.9a12/lib/log/law/sites-tempban/
ds2.9a12/lib/log/law/sites-watch/
ds2.9a12/lib/log/open/
ds2.9a12/lib/log/reports/
ds2.9a12/lib/log/router/
ds2.9a12/lib/log/secure/
ds2.9a12/lib/log/watch/
ds2.9a12/lib/obj/book_source/
ds2.9a12/lib/obj/include/
ds2.9a12/lib/powers/prayers/
ds2.9a12/lib/powers/spells/
ds2.9a12/lib/realms/template/adm/
ds2.9a12/lib/realms/template/area/armor/
ds2.9a12/lib/realms/template/area/npc/
ds2.9a12/lib/realms/template/area/obj/
ds2.9a12/lib/realms/template/area/room/
ds2.9a12/lib/realms/template/area/weap/
ds2.9a12/lib/realms/template/bak/
ds2.9a12/lib/realms/template/cmds/
ds2.9a12/lib/save/kills/o/
ds2.9a12/lib/secure/cfg/classes/
ds2.9a12/lib/secure/cmds/builders/
ds2.9a12/lib/secure/cmds/creators/include/
ds2.9a12/lib/secure/cmds/players/
ds2.9a12/lib/secure/cmds/players/include/
ds2.9a12/lib/secure/daemon/imc2server/
ds2.9a12/lib/secure/daemon/include/
ds2.9a12/lib/secure/lib/
ds2.9a12/lib/secure/lib/include/
ds2.9a12/lib/secure/lib/net/include/
ds2.9a12/lib/secure/lib/std/
ds2.9a12/lib/secure/log/adm/
ds2.9a12/lib/secure/log/bak/
ds2.9a12/lib/secure/log/intermud/
ds2.9a12/lib/secure/log/network/
ds2.9a12/lib/secure/modules/
ds2.9a12/lib/secure/npc/
ds2.9a12/lib/secure/obj/include/
ds2.9a12/lib/secure/room/
ds2.9a12/lib/secure/save/
ds2.9a12/lib/secure/save/backup/
ds2.9a12/lib/secure/save/boards/
ds2.9a12/lib/secure/tmp/
ds2.9a12/lib/secure/upgrades/files/
ds2.9a12/lib/secure/verbs/creators/
ds2.9a12/lib/std/board/
ds2.9a12/lib/std/lib/
ds2.9a12/lib/tmp/
ds2.9a12/lib/verbs/admins/include/
ds2.9a12/lib/verbs/builders/
ds2.9a12/lib/verbs/common/
ds2.9a12/lib/verbs/common/include/
ds2.9a12/lib/verbs/creators/
ds2.9a12/lib/verbs/creators/include/
ds2.9a12/lib/verbs/rooms/
ds2.9a12/lib/verbs/rooms/include/
ds2.9a12/lib/www/client/
ds2.9a12/lib/www/errors/
ds2.9a12/lib/www/images/
ds2.9a12/lib/www/lpmuds/downloads_files/
ds2.9a12/lib/www/lpmuds/intermud_files/
ds2.9a12/lib/www/lpmuds/links_files/
ds2.9a12/win32/
string *blacklisted_muds = ({});
static string *graylisted_muds = ({});
static mapping packet_counter = ([]); 

void read_callback(mixed fd, mixed info){
    // This is called when messages come in from a MUD.
    // Should reject all messages if they have not done a (successful) startup-req,
    // Should check to make sure the fd matches with the mud they are claiming to be, else error.
    // because all packets require an originator_mudname.

    // If target is the name of the router, then call the function in this object.
    // if target is 0, then broadcast to muds.
    // Do an error if target mudname is not known.
    // 

    // According to: http://www.intermud.org/i3/specs/formats.php3
    // Transmissions are LPC arrays with a predefined set of six initial elements:
    // ({ type, ttl, originator mudname, originator username, target mudname, target username, ... }). 
    // info[0]=type
    // info[1]=ttl
    // info[2]=originator mudname
    // info[3]=originator username
    // info[4]=target mudname
    // info[5]=target username

    string mudname;
    int i;
    string *logspam = ({ "auth-mud-req", "auth-mud-reply", "tell", "emoteto",
      "ping","channel-listen", "ping-req", "ping-reply" });
    string ip_addr = (stringp(fd) ? fd : clean_fd(socket_address(fd)));

    if(!packet_counter) packet_counter = ([]);

    //trr("read_callback, fd"+fd+", info: "+identify(info));
    if(!strsrch(info[0],"irn-")){
        //trr("spotted irn packet: "+info[0],"blue");
        irn_read_callback(fd, info);
        return;
    }

    if(stringp(fd) && member_array(info[0],logspam) == -1)
        //trr("Received IRN data "+info[0]+" for "+info[4]+" from "+info[2]);

        if(info[4] && !grepp(info[0],"startup-req") && 
          member_array(info[4], keys(connected_muds)) == -1 &&
          member_array(info[2], keys(connected_muds)) == -1){
            //trr(info[4]+" is not on this router. Dropping from read_callback.");
            return;
        }
    validate();
    //tc("passed validation");
    if(!packet_counter[info[2]]){
        //tc("Adding "+info[2]+" to packet_counter");
        packet_counter[info[2]] = ([ "time" : time(), "count" : 0 ]);
    }
    if(packet_counter[info[2]]["time"] != time()){
        packet_counter[info[2]]["count"] = 0;
        packet_counter[info[2]]["time"] = time();
    }
    if(info[0] != "channel-listen"){
        packet_counter[info[2]]["count"]++;
    }
    //tc(info[2]+" count: "+packet_counter[info[2]]["count"]);
    if(packet_counter[info[2]]["count"] > 10 &&
      member_array(fd, keys(ROUTER_D->query_irn_sockets())) == -1){
        if(true()){
            if(info[2] && member_array(info[2], keys(connected_muds)) != -1){
                send_error(info[2],info[3],"not-allowed",
                  "Flood detected! Your packets will be temporarily ignored.",
                  info);
            }
            graylisted_muds += ({ ip_addr });
            return;
            tc("Flood detected from "+ip_addr,"red");
        }
    }
    if(sizeof(blacklisted_muds)){
        if(member_array(info[2], blacklisted_muds) != -1) return;
        if(intp(fd) && member_array(ip_addr, blacklisted_muds) != -1) return;
    }
    if(sizeof(graylisted_muds)){
        if((info[2] && member_array(info[2], graylisted_muds) != -1) ||
          (intp(fd) && member_array(ip_addr, graylisted_muds) != -1)){ 
            if(info[2] && member_array(info[2], keys(connected_muds)) != -1){
                send_error(info[2],info[3],"not-allowed",
                  "Your packets are being temporarily dropped due to flooding.",
                  info);
            }
            tc("Dropping packet from "+ip_addr,"yellow");
            return;
        }
    }
    if(member_array(info[0],logspam) == -1 && strsrch(info[0],"irn-")){
        string foo = "IRN";
        if(intp(fd)) foo = socket_address(fd);
        trr(timestamp()+" Received from fd("+fd+"), source: "+foo+"\n"+identify(info), "green");
    }
    // Base info in a packet is of size 6.
    // comment out the statements below to avoid a big channel log file
    if(grepp(info[0],"chan") && !grepp(info[0],"channel-listen")) 
        server_log(identify(info),"chan_log");

    if(sizeof(info)<6 ||
      !stringp(info[0]) ||
      !intp(info[1]) || !stringp(info[2]) ||
      (!stringp(info[3]) && info[3]!=0) ||
      (!stringp(info[4]) && info[4]!=0) ||
      (!stringp(info[5]) && info[5]!=0) ) {
        write_data(fd,({
            // okay, their initial data isn't fully there...
            // careful about array out of bounds...
            "error",5,router_name,0,
            (sizeof(info)>=3 ? info[2] : 0), // mud name
            (sizeof(info)>=4 ? info[3] : 0), // user name
            "bad-pkt","Invalid initial data",info
          }));
        return;
    }
    if(info[4]!=0 && !mudinfo[info[4]] && info[4]!=router_name){
        // if target mud is not 0 (broadcasting), not the router name,
        // and not a known MUD
        write_data(fd,({
            "error",
            5,
            router_name,
            0,
            info[2],
            info[3],
            "unk-dst", // same as I3
            "destination unknown", // same as I3
            info
          }));
        trr("Error [unk-dst], because target is "+info[4]+" and thus invalid.");
        return;
    }
    if(sscanf(info[0],"startup-req-%d",i)==1){
        // special condition for startup-req...
        trr("calling process_startup_req, i="+i+", fd="+fd+" which is: "+socket_address(fd));
        //call_other(this_object(),"process_startup_req",i,info,fd);
        this_object()->process_startup_req(i,info,fd);
        return;
    }
    if(intp(fd) && connected_muds[info[2]] && (connected_muds[info[2]]!=fd)){
        // MUD hasn't done a startup-req yet
        write_data(fd,({
            "error",
            5,
            router_name,
            0, // originator username
            info[2], // mud name
            info[3], // user name
            "unk-src", // error code
            "Your MUD hasn't registered as "+info[2]+" yet", // Error message
            info
          }));
        server_log("They have not done a startup-req for fd="+fd+", mudname="+info[2]);
        return;
    }
    // at this point, I guess it has a valid origin and stuff
    if(sscanf(info[0],"channel-%*s")==1){ // command has a "channel-" prefix
        // special case for channel stuff
        //trr("calling process_channel...");
        process_channel(fd,info);
        return;
    }
    if(info[0]=="chan-filter-reply"){
        if(!stringp(info[6]) || sizeof(info[7])<9 ){
            //trr("wtf2");
            // avoid out-of-bounds
            send_error(info[2],info[3],"bad-pkt",
              "Invalid chan-filter-reply packet.",info);
        }
        if(channels[info[6]][1]!=info[2]){
            send_error(info[2],info[3],"not-allowed",
              "You are not the owner of "+info[6],info);
            return;
        }
        if(member_array(info[7][6],({ "channel-a", "channel-e", "channel-t"}))==-1){
            // Not a valid channel packet.
            send_error(info[2],info[3],"not-allowed","*giggles*",info);
        }
        if(info[6]!=info[7][6]){
            // They're trying to trick me into broadcasting
            // fake messages on another channel!
            // That's pretty funny :P
            // I'll have to remember to check if the official router falls for it.
            send_error(info[2],info[3],"not-allowed","Hehe!",info);
            return;
        }
        if(member_array(info[7][6],({ "channel-a", "channel-e", "channel-t"}))){
        }
        // Broadcast the message...
        if(intp(fd)) this_object()->SendMessage(info);
        foreach(mudname in keys(connected_muds)){
            if(member_array(mudname, listening[info[6]])!=-1)
                write_data(connected_muds[mudname],info[7]);
        }
        return;
    }
    if(info[4]==0){ // if broadcasting this...
        if(intp(fd)) {
            this_object()->SendMessage(info);
        }
        broadcast_data(connected_muds,info);
        return;   
    }
    if(info[4]==router_name) {
        switch(info[0]){
        case "shutdown" :
            if(sizeof(info) != 7){
                send_error(info[2],info[3],"bad-pkt","Wrong number of elements for packet: "+info[0],info);
                return;
            }
            if(info[6] > 604800) {
                server_log("ROUTER_D: deleting "+this_object()->query_connected_fds()[fd]+" per "+
                  "request of "+info[2]+"\n");
                remove_mud(this_object()->query_connected_fds()[fd], 1);
            }
            else {
                server_log("ROUTER_D: disconnecting "+this_object()->query_connected_fds()[fd]+" per "+
                  "request of "+info[2]);
                this_object()->disconnect_mud(this_object()->query_connected_fds()[fd]);
            }
            break;
        case "chanlist-req" : case "chanlist-request" :
            broadcast_chanlist("foo",info[2]);
            break;
        default :
            // Something meant for the router but not handled by now!
            send_error(info[2],info[3],"not-imp","Unknown command sent to router: "+info[0],info);
            server_log("%^RED%^UNHANDLED PACKET:\n"+identify(info));
        }
        return;
    }
    if(info[0]=="startup-reply" || info[0]=="chan-filter-reply"||
      info[0]=="chanlist-reply" || info[0]=="mudlist" ||
      info[0]=="bad-mojo"){
        // Thanks to Tricky for pointing out the need for this safeguard
        send_error(info[2],info[3],"not-allowed",
          "You are not allowed to send this kind of packet.",info);
        return;
    }
    // at this point, I guess you should forward it to the destination...
    if(info[0]=="locate-reply"){
        // Special case for locate-reply, because protocol 3 has a larger packet...
        if(sizeof(info)==8 && mudinfo[info[4]]["protocol"]>2){
            // originator mud is sending a protocol 1/2 response,
            // but target understands 3, so add the extra info
            write_data(connected_muds[info[4]], ({
                info[0],info[1],info[2],info[3],info[4],info[5],
                info[6],info[7],0,0
              }));
            return;
        }	if(sizeof(info)==10 && mudinfo[info[4]]["protocol"]<=2){
            // target mud is being sent a protocol 3 response,
            // but only understands 1 & 2, so strip the extra info
            write_data(connected_muds[info[4]], ({
                info[0],info[1],info[2],info[3],info[4],info[5],
                info[6],info[7]
              }));
            return;
        }
    }
    // if fd is an integer (meaning it's locally connected) and info[4] is zero
    // (meaning it's a boradcast) then we send data to irn. Also, if fd is an
    // integer, info[4] is non-zero, and info[4] is not a connected mud.
    if((!info[4] && intp(fd)) || 
      (intp(fd) && info[4] && !connected_muds[info[4]]) ) this_object()->SendMessage(info);
    write_data(connected_muds[info[4]], info);
    return;
}