/* * UDP port handling code. Version 0.4.3 * Written by Nostradamus for Zebedee. * Developed from an original concept by Alvin@Sushi. */ #include <master.cfg> #include <udp.h> #undef DATE #ifdef ZEBEDEE #include <defs.h> #elif !defined(DATE) #define DATE ctime(time())[4..15] #endif #define MAX_PACKET_LEN 1024 #define UNKNOWN 0 #define UP time() #define DOWN (-time()) #define DELIMITER "|" private mapping hosts, pending_data, incoming_packets; private int packet_id; void set_host_list(); varargs int send_udp(string mudname, mapping data, int expect_reply); #ifdef DEBUG void debug(string msg) { object ob; if (ob = find_player("nostradamus")) tell_object(ob, "Debug: " + msg); } #endif /* DEBUG */ #if !defined(COMPAT_FLAG) || defined(ZEBEDEE) void create() { #else void reset(mixed arg) { if (arg) return; #endif pending_data = ([ ]); incoming_packets = ([ ]); set_host_list(); } void set_host_list() { mixed data; hosts = ([ ]); if (data = read_file(HOST_FILE)) { int i; data = explode(data, "\n") - ({ "" }); for(i = sizeof(data); i--; ) { if (data[i][0] == '#') continue; data[i] = explode(data[i], ":"); hosts[lower_case(data[i][HOST_NAME])] = ({ capitalize(data[i][HOST_NAME]), data[i][HOST_IP], to_int(data[i][HOST_UDP_PORT]), data[i][HOST_COMMANDS..], UNKNOWN }); send_udp(data[i][HOST_NAME], ([ REQUEST: PING ]), 1); } } } private mixed decode_packet(string packet) { string *data; mapping ret; string class, info; int i; data = explode(packet, DELIMITER); if (data[0][0..strlen(PACKET)] == PACKET + ":") { int id, n; if (sscanf(data[0][strlen(PACKET)+1..], "%s:%d:%d/%d", class, id, i, n) != 4) return 0; class = lower_case(class) + ":" + id; if (pointerp(incoming_packets[class])) { incoming_packets[class][i-1] = packet[strlen(data[0])+1..]; if (member_array(0, incoming_packets[class]) == -1) { ret = decode_packet(implode(incoming_packets[class], "")); incoming_packets = m_delete(incoming_packets, class); return ret; } } else { incoming_packets[class] = allocate(n); incoming_packets[class][i-1] = packet[strlen(data[0])+1..]; if (!pending_data[class]) call_out("incoming_time_out", TIME_OUT, class); } return 1; } ret = ([ ]); for(i = 0; i < sizeof(data); i++) { if (sscanf(data[i], "%s:%s", class, info) != 2) return 0; switch(class) { case DATA: info = implode(data[i..], DELIMITER)[strlen(DATA)+1..]; break; case REQUEST: case SENDER: case RECIPIENT: info = lower_case(info); break; } if (info[0] == '$') ret[class] = info[1..]; else if ((string)(ret[class] = (int)info) != info) ret[class] = info; if (class == DATA) return ret; } return ret; } private int valid_request(mapping data) { mapping host_data; if (!data[NAME] || !(host_data = hosts[lower_case(data[NAME])])) { log_file(LOG_FILE, DATE + ": Unknown mud.\n"); return 0; } if (data[HOST] != host_data[HOST_IP]) { log_file(LOG_FILE, DATE + ": Host mismatch.\n"); return 0; } if (!data[REQUEST] || (data[REQUEST] != REPLY && data[REQUEST] != PING && member_array("*", host_data[HOST_COMMANDS]) == -1 && member_array(data[REQUEST], host_data[HOST_COMMANDS]) == -1)) { log_file(LOG_FILE, DATE + ": Illegal command.\n"); return 0; } return 1; } void receive_udp(string sender, string packet) { mapping data; string err; #if 0 if (!previous_object() || file_name(previous_object()) != __MASTER_OBJECT__) return; #endif if (!mappingp(data = decode_packet(packet))) { if (!data) log_file(LOG_FILE, DATE + ": Received invalid packet.\nSender: " + sender + "\nPacket:\n" + packet + "\n\n"); return; } data[HOST] = sender; if (!valid_request(data)) { log_file(LOG_FILE, "Sender: " + sender + "\nPacket:\n" + packet + "\n\n"); return; } switch(data[REQUEST]) { case PING: hosts[lower_case(data[NAME])][HOST_STATUS] = UP; send_udp(data[NAME], ([ REQUEST: REPLY, ID: data[ID] ]) ); break; case REPLY: pending_data = m_delete(pending_data, lower_case(data[NAME]) + ":" + data[ID]); if (data[RECIPIENT]) { object ob; if (ob = find_player(data[RECIPIENT])) tell_object(ob, "\n" + data[DATA]); else if (err = catch( data[RECIPIENT]->udp_reply(copy_mapping(data)))) log_file(LOG_FILE, DATE + ": Error in file: " + data[RECIPIENT] + "\n" + err + "\n"); } else if (data[DATA]) log_file(LOG_FILE, DATE + ": Reply from " + data[NAME] + "\n" + data[DATA] + "\n\n"); break; default: { int ret; if ((err = catch(ret = call_other( UDP_CMD_DIR + data[REQUEST], "udp_" + data[REQUEST], copy_mapping(data)))) || !ret) { send_udp(data[NAME], ([ REQUEST: REPLY, RECIPIENT: data[SENDER], ID: data[ID], DATA: "Root@" + LOCAL_NAME + ": " + capitalize(data[REQUEST]) + " request failed.\n" ]) ); log_file(LOG_FILE, DATE + ": " + data[REQUEST] + " failed.\n" + (err ? err : "No error.") + "\n"); } break; } } if (hosts[lower_case(data[NAME])]) hosts[lower_case(data[NAME])][HOST_STATUS] = UP; } private int match_mud_name(string mudname, string match_str) { return mudname[0..strlen(match_str)-1] == match_str; } private string encode_packet(mapping data) { int i; mixed indices, tmp; string ret; for(i = sizeof(indices = m_indices(data)); i--; ) { if (indices[i] == DATA || !data[indices[i]]) continue; if (pointerp(tmp = data[indices[i]])) tmp = file_name(tmp); else if (stringp(tmp) && (tmp[0] == '$' || (string)to_int(tmp) == (string)tmp)) tmp = "$" + tmp; if (ret) ret += DELIMITER + indices[i] + ":" + tmp; else ret = indices[i] + ":" + tmp; } if (ret) { if (data[DATA]) ret += DELIMITER + DATA + ":" + data[DATA]; return ret; } } string *explode_packet(string packet, int len) { if (strlen(packet) <= len) return ({ packet }); return ({ packet[0..len-1] }) + explode_packet(packet[len..], len); } varargs int send_udp(string mudname, mapping data, int expect_reply) { mixed host_data; string *packet_arr; string packet; int i; string file; if(previous_object() && sscanf(file_name(previous_object()),UDP_CMD_DIR+"%s",file) != 1) { return 0; /* cmd from illegal object */ } mudname = lower_case(mudname); if (!(host_data = hosts[mudname])) { string *names; if (sizeof(names = filter_array( m_indices(hosts), "match_mud_name", this_object(), mudname)) == 1) host_data = hosts[names[0]]; else { write("Unknown mud: " + mudname + "\n"); return 0; } } if (data[REQUEST] != PING && data[REQUEST] != REPLY && member_array("*", host_data[HOST_COMMANDS]) == -1 && member_array(data[REQUEST], host_data[HOST_COMMANDS]) == -1) { write(capitalize(data[REQUEST]) + ": Command unavailable @" + host_data[HOST_NAME] + "\n"); return 0; } if (expect_reply) { /* Don't use zero. */ packet_id++; pending_data[lower_case(host_data[HOST_NAME]) + ":" + packet_id] = data + ([ NAME: host_data[HOST_NAME] ]); call_out("reply_time_out", TIME_OUT, lower_case(host_data[HOST_NAME]) + ":" + packet_id); data[ID] = packet_id; } data += ([ NAME: LOCAL_NAME, UDP_PORT: LOCAL_UDP_PORT ]); if (!(packet = encode_packet(data))) { if (expect_reply) pending_data = m_delete(pending_data, lower_case(host_data[HOST_NAME]) + ":" + packet_id); write("inetd: Illegal packet.\n"); log_file(LOG_FILE, DATE + ": Illegal packet sent by " + file_name(previous_object()) + "\n\n"); return 0; } if (strlen(packet) <= MAX_PACKET_LEN) packet_arr = ({ packet }); else { string header; int max; header = PACKET + ":" + lower_case(LOCAL_NAME) + ":" + (expect_reply ? packet_id : ++packet_id) + ":"; packet_arr = explode_packet(packet, MAX_PACKET_LEN - (strlen(header) + 8)); for(i = max = sizeof(packet_arr); i--; ) packet_arr[i] = header + (i+1) + "/" + max + DELIMITER + packet_arr[i]; } for(i = sizeof(packet_arr); i--; ) { if (!send_imp( host_data[HOST_IP], (int)host_data[HOST_UDP_PORT], packet_arr[i])) { write("inetd: Error in sending packet.\n"); return 0; } } return 1; } private void reply_time_out(mixed id) { mapping data; if (data = pending_data[id]) { if (data[REQUEST] != PING) { object ob; if (data[SENDER] && (ob = find_player(data[SENDER]))) tell_object(ob, "\ninetd: " + capitalize(data[REQUEST]) + " request to " + (data[RECIPIENT] ? capitalize(data[RECIPIENT]) + "@" + data[NAME] : data[NAME]) + " timed out.\n"); } if (hosts[lower_case(data[NAME])]) hosts[lower_case(data[NAME])][HOST_STATUS] = DOWN; /* Should this be outside the if() ? */ incoming_packets = m_delete(incoming_packets, lower_case(data[NAME]) + ":" + id); } pending_data = m_delete(pending_data, id); } private incoming_time_out(string id) { incoming_packets = m_delete(incoming_packets, id); } mixed query(string what) { switch(what) { case "hosts": return hosts; case "pending": return pending_data; case "incoming": return incoming_packets; } }