cat /adm/daemons/network/I3.c /* InterMud 3 client by Drizzt@Tmi-2 // Leto july 6th 95 - Tried to add some security :) // Brainstorm May 96 - Added some more support for I3 channels. */ #include #include #include #define I3_DEBUG_OUT 0x01 #define I3_DEBUG_IN 0x02 #define I3_DEBUG_LST 0x04 static object router_socket, debugger; static private int oob_socket; static mapping services; static int debug_level; static int bootflag; // Used to recognize the first channel list we get. int router_password, mudlist_id, chanlist_id; string *router_list; static private mapping oob_sockets=([]); // [fd] as key, [mudname, type, finished] as value static mapping authed=([]); // [fd] as key, [mudname] as value mixed query_router_list(){ return copy(router_list); } mapping mudlist, chanlist; // Prototypes :) void create(); void check_router(); static void oob_read_callback(object socket, mixed info); static void oob_close_callback(object socket); static void oob_close_connection(int fd); static void oob_write_data(int fd, mixed data); void set_debugging(int level, object person){ // Prevent people from snooping private stuff if(!adminp(person)) return; debugger = person; debug_level = level; } void debug(string str){ if(find_player("tim")) tell_object(find_player("tim"), str+"\n"); } mixed *query_debugging(){ return ({ debugger, debug_level }); } static private void debug_stuff(mixed message){ if(debugger) message("I3",sprintf("%c[1;32m[I3]: %O%c[0m\n", 27, message, 27), debugger); } string query_network_name(string mud){ string *names; int found,i; if(mud == "global") return 0; if(mud && mudlist){ mud = replace_string(mud,"."," "); /* Match in the Mudlist for a correct name */ mud = lower_case(mud); names = keys(mudlist); found = 0; for(i=0;iremove(); create(); return 1; } int send_packet(string type, string user, string mud, string target, mixed *data){ mixed *send, *names; int i,found; // Leto: Add origin()==LOCAL_ORGIN || base_name(prev_obj)[0..x] // == "/adm/daemons/network/I3/" check here. if(!query_network_name(mud) && mud != "global" && mud != 0 && mud != ROUTER_NAME ) return 0; if( mud != ROUTER_NAME ) mud = query_network_name(mud); send = ({ type, I3_TTL, MUD_NAME, user, mud, target }) + data; if(debug_level & I3_DEBUG_OUT) debug_stuff(send); router_socket->send(send); return 1; } static private int send_chanlist_req(){ send_packet(PRT_CHANLIST_REQ, 0, ROUTER_NAME, 0, ({ chanlist_id }) ); } // Leto: I can't find anything calling this function. void process_chanlist_reply(mixed *info){ int i; string *kys; if(sizeof(info) != SIZ_CHANLIST_REPLY) return; chanlist_id = info[6]; if(!chanlist) chanlist = ([]); // Brainstorm TEST HACK! was /* */ kys = keys(chanlist); if(find_player("tim")) message("I3","chanlist-reply with channels: "+identify(keys(info[7]))+"\n",find_player("tim")); // At the moment the router only sends complete lists // so this might actually work. if( mapp( info[7] ) ) for( i=0;idelete_channel( kys[i] ); if(debug_level & I3_DEBUG_LST) debug_stuff("Removing Channel \""+kys[i]+"\"\n" ); } chanlist += info[7]; kys = keys(chanlist); /* Clean up deleted channels */ for(i=0;idelete_channel( kys[i] ); if(debug_level & I3_DEBUG_LST) debug_stuff("Removing Channel \""+kys[i]+"\"\n" ); } } // Bootflag == 1 means the CHANNELS_D shall tune us in to // the channels users are listening to. CHANNELS_D->add_i3channels( chanlist, bootflag ); bootflag=0; save_object(I3_SAVE_FILE); } /* static private int send_startup_req_1() { mixed *packet; packet = ({ router_password, mudlist_id, chanlist_id, __PORT__, OOB_TCP, OOB_UDP, I3_MUDLIB, I3_BASE_LIB, __VERSION__, "LPmud", MUD_STAGE, services }); send_packet(PRT_STARTUP_REQ_3,0,ROUTER_NAME,0,packet); } */ static private int send_startup_req_3() { mixed *packet; packet = ({ router_password, mudlist_id, chanlist_id, __PORT__, OOB_TCP, OOB_UDP, I3_MUDLIB, I3_BASE_LIB, __VERSION__, "LPmud", MUD_STAGE, I3_ADMIN_EMAIL, services, I3_EXTRA }); send_packet(PRT_STARTUP_REQ_3,0,ROUTER_NAME,0,packet); } // Leto: This function isn't called either void process_startup_reply(mixed *info){ if(sizeof(info) != SIZ_STARTUP_REPLY) return; router_password = info[7]; router_list = info[6]; save_object(I3_SAVE_FILE); } /* Internal Mudlist Routines: We handle all of them in here because this way we can deal with only 1 object to save, instead of saving a module, and this object */ void process_mudlist(mixed *info){ string *kys; int i; if(sizeof(info) != SIZ_MUDLIST) return; mudlist_id = info[6]; if(!mudlist) mudlist = ([]); mudlist += info[7]; kys = keys(mudlist); /* Clean up deleted channels */ for(i=0;idelete_channel( error[6] ); } break; case "not-allowed": // Not allowed to tune in to channel, remove from CHANNELS_D -added by Tim error=info[8]; if( (string)error[0] == PRT_CHANNEL_LISTEN ) { if(debug_level & I3_DEBUG_LST) debug_stuff("Removing Channel \""+error[6]+"\" because can't tune in to it.\n" ); CHANNELS_D->delete_channel( error[6] ); } break; default: break; } if( info[5] ) ob = find_living(info[5]); if (!ob && debugger) ob = debugger; if (!ob) log_file("I3","I3 Error from "+info[2]+": "+info[7]+"---"+identify(info[8])+"\n"); else message("I3","I3 Error from "+info[2]+": "+info[7]+"\n", ob); } void setup_services(){ mixed dir; string name; int i; services = ([]); #ifdef I3_SERVICE_DETECT dir = get_dir(I3_MODULES); for(i=0;iremove(); if(router_socket) router_socket->remove(); // Set boot flag on create, delete it on channel list processing. bootflag=1; setup_services(); router_socket = clone_object(SOCKET, SKT_STYLE_CONNECT_M, I3_ROUTER, (: read_callback :), (: close_callback :) ); // oob_socket = clone_object(SOCKET, SKT_STYLE_LISTEN_M, OOB_TCP, // (: oob_read_callback :), // (: oob_close_callback :) ); send_startup_req_3(); // send_startup_req_1(); // New... if ((oob_socket = socket_create(MUD, "oob_read_callback", "oob_close_callback")) < 0){ log_file("oob", "setup: Failed to create socket.\n"); return; } if (socket_bind(oob_socket, OOB_TCP) < 0) { socket_close(oob_socket); log_file("oob", "setup: Failed to bind socket to port.\n"); return; } if (socket_listen(oob_socket, "oob_listen_callback") < 0) { socket_close(oob_socket); log_file("oob", "setup: Failed to listen to socket.\n"); } // ...New // [Deathblade] commented this damn thing out :-). There is // no such packet. // send_chanlist_req(); call_out( (: check_router :) , 900 ); } void remove(){ int i; /* foreach(i in keys(oob_socket)){ map_delete(oob_socket); } if(oob_socket) oob_socket->remove(); don't need to do anything? */ if(router_socket) router_socket->remove(); destruct(this_object()); } void check_router() { if(!router_socket || !oob_socket) { log_file("I3","Lost I3 router, reconnecting at "+ctime(time())+".\n"); create(); return; } call_out( (: check_router :) , 900 ); } static int filter_mudlistwatch(object ob){ if(!adminp(ob)) return 0; if(!ob->query_env("tell_mudlist")) return 0; return 1; } static void oob_read_callback(int fd, mixed info){ string fname; // file name string func; // function name debug("oob_read_callback fd: "+fd+", info: "+identify(info)+"\n"); if(sizeof(info)<1) return; if(!stringp(info[0])) return; if(info[0]=="oob-begin"){ // 0=oob-begin // 1=originator mudname // 2=auth_type... 0=no auth, 1=auth-mud-req used // 3=auth_token if(sizeof(info)<2){ debug("AUTH: bad packet!"); return; } if(info[2]==1){ if(!I3_AUTH->query_auth(info[1],info[3])){ debug("AUTH: rejected!"); oob_close_connection(fd); return; } authed[fd]=info[1]; } // valid here? debug("OOB-BEGIN: success!"); // add it to connected muds list... oob_sockets[fd]=({info[1],info[2],0}); // name, not finished // send oob-begin with empty authorization info to // tell originator to begin... oob_write_data(fd,({"oob-begin",MUD_NAME,0,0})); return; } if(info[0]=="oob-end"){ oob_sockets[fd][2]=1; // finished: on } debug("is not an auth..."); func = "oob_process_"+info[0]; func = replace_string(func,"-","_"); if(!sscanf(info[0], "%s-%*s",fname)) fname = info[0]; debug("calling "+func+" in "+(I3_MODULES+fname)+"."); call_other(I3_MODULES+fname, func, fd, info); return; // quit // if haven't done an auth yet... } static void oob_close_callback(int fd){ if(find_player("tim")) tell_object(find_player("tim"), "oob_close_callback socket: "+fd+"\n"); map_delete(authed, fd); } static void oob_listen_callback(int fd){ int nfd; debug("oob_listen_callback: fd="+fd+"\n"); if ((nfd = socket_accept(fd, "oob_read_callback", "oob_write_callback")) < 0) { // log_file("server", "oob_listen_callback: socket_accept failed.\n"); return; } } static void oob_close_connection(int fd){ int errcode; // debug("Attempting to close socket\n"); map_delete(oob_sockets, fd); errcode = socket_close(fd); // debug("Socket closed with code:"+errcode+".\n"); } void oob_write_data(int fd, mixed data){ // should check if previous_object is in the services dir debug(sprintf("oob_write_data: fd=%d, data=%s",fd,identify(data))); socket_write(fd, data); } string get_auth(int fd){ if(!authed[fd]) return 0; return authed[fd]; }