MG Mud User | 88f1247 | 2016-06-24 23:31:02 +0200 | [diff] [blame^] | 1 | /* |
| 2 | * UDP port handling code. Version 0.7a |
| 3 | * Written by Nostradamus for Zebedee. |
| 4 | * Developed from an original concept by Alvin@Sushi. |
| 5 | */ |
| 6 | |
| 7 | #pragma strict_types |
| 8 | #pragma no_clone |
| 9 | #pragma no_shadow |
| 10 | #pragma no_inherit |
| 11 | #pragma verbose_errors |
| 12 | //#pragma pedantic |
| 13 | #pragma no_range_check |
| 14 | #pragma no_warn_deprecated |
| 15 | |
| 16 | |
| 17 | #include <living/comm.h> |
| 18 | #define ZDEBUG(x) if (find_player("zesstra"))\ |
| 19 | find_player("zesstra")->ReceiveMsg(x, \ |
| 20 | MT_DEBUG|MSG_DONT_STORE,0,"IM: ",this_object()) |
| 21 | |
| 22 | //#define ZDEBUG(x) |
| 23 | |
| 24 | #include <udp.h> |
| 25 | |
| 26 | /* --- Configurable definitions. --- */ |
| 27 | |
| 28 | /* CD muds will probably need these include file. */ |
| 29 | /* #include <std.h> */ |
| 30 | /* #include "/config/sys/local.h" */ |
| 31 | |
| 32 | /* Public commands which will be accessible to any unlisted muds. |
| 33 | * PING, QUERY and REPLY are included by default. */ |
| 34 | #define COMMANDS \ |
| 35 | ({ "channel", "finger", "tell", "who", "mail", "www", "htmlwho", "locate" }) |
| 36 | |
| 37 | //({ "channel", "finger", "ftp", "locate", "man", "tell", "who" }) |
| 38 | |
| 39 | /* Define this to the object that receives incoming packets and passes |
| 40 | * them to the inetd. Undefine for no _receive_udp() security. |
| 41 | * NOTE: The string must be of the format that object_name() returns. */ |
| 42 | #define UDP_MASTER __MASTER_OBJECT__ |
| 43 | |
| 44 | /* How to set the euid for this object if using native mode. |
| 45 | * Ensure that it can read the INETD_HOSTS file. */ |
| 46 | #define SET_EUID seteuid(getuid()) |
| 47 | |
| 48 | /* Define this if you are running another intermud package concurrently. */ |
| 49 | /* #define RECEIVE_UDP_COMPAT(sender, packet) \ |
| 50 | UDP_MANAGER->incoming_udp(sender, packet) // CD */ |
| 51 | |
| 52 | /* Define this if you are running another intermud package concurrently and |
| 53 | * have a compatability module for it. */ |
| 54 | /* #define SEND_UDP_COMPAT(mudname, data, expect_reply) \ |
| 55 | "/secure/udp_compat"->_send_udp(mudname, data, expect_reply) */ |
| 56 | |
| 57 | /* The maximum number of characters we can send in one packet. |
| 58 | * You may need to reduce this, but 512 should be safe. */ |
| 59 | #define MAX_PACKET_LEN 1024 |
| 60 | |
| 61 | /* You shouldn't need to change anything below. */ |
| 62 | |
| 63 | #define USE_OLD_DELIMITER |
| 64 | //#define DELIMITER_COMPAT |
| 65 | #define USE_OLD_DATA_FORMAT |
| 66 | |
| 67 | #ifdef ZEBEDEE |
| 68 | #include <defs.h> |
| 69 | #endif |
| 70 | |
| 71 | #ifndef DATE |
| 72 | #define DATE ctime(time())[4..15] |
| 73 | #endif |
| 74 | |
| 75 | /* --- End of Config. Do not alter anything below. --- */ |
| 76 | |
| 77 | #define UNKNOWN 0 |
| 78 | #define UP time() |
| 79 | #define DOWN (-time()) |
| 80 | |
| 81 | #define NEW_DELIMITER "\n" |
| 82 | #ifdef USE_OLD_DELIMITER |
| 83 | #define DELIMITER "|" |
| 84 | #else |
| 85 | #define DELIMITER "\n" |
| 86 | #endif |
| 87 | #define OLD_DELIMITER "|" |
| 88 | #define HOSTFILE_DEL ":" |
| 89 | #define HOSTFILE_DEL2 "," |
| 90 | #define RETRY "_RETRY" |
| 91 | |
| 92 | private nosave mapping hosts, pending_data, incoming_packets; |
| 93 | private nosave mapping static_host_list; |
| 94 | private nosave string *received_ids; |
| 95 | private nosave int packet_id; |
| 96 | |
| 97 | void set_host_list(); |
| 98 | varargs string _send_udp(string mudname, mapping data, int expect_reply); |
| 99 | |
| 100 | #define DEBUG(msg) |
| 101 | |
| 102 | void my_create() { |
| 103 | #ifndef COMPAT_FLAG |
| 104 | SET_EUID; |
| 105 | #endif |
| 106 | packet_id = 0; |
| 107 | pending_data = ([ ]); |
| 108 | incoming_packets = ([ ]); |
| 109 | hosts = ([ ]); |
| 110 | received_ids = ({ }); |
| 111 | set_host_list(); |
| 112 | if (!this_player()) |
| 113 | call_out("startup", 1, 0); |
| 114 | } |
| 115 | |
| 116 | #ifdef CREATE_FUN |
| 117 | CREATE_FUN() { |
| 118 | #elif !defined(COMPAT_FLAG) || defined(ZEBEDEE) |
| 119 | void create() { |
| 120 | #else |
| 121 | void reset(arg) { |
| 122 | if (arg) |
| 123 | return; |
| 124 | #endif |
| 125 | my_create(); |
| 126 | } |
| 127 | |
| 128 | status check_system_field(mapping data, string field) { |
| 129 | return data[SYSTEM] && member(data[SYSTEM], field) != -1; |
| 130 | } |
| 131 | |
| 132 | mapping add_system_field(mapping data, string field) { |
| 133 | if (data[SYSTEM]) { |
| 134 | if (!check_system_field(data, field)) |
| 135 | data[SYSTEM] += ({ field }); |
| 136 | } |
| 137 | else |
| 138 | data[SYSTEM] = ({ field }); |
| 139 | return data; |
| 140 | } |
| 141 | |
| 142 | private mapping read_host_list(string file) { |
| 143 | |
| 144 | mixed data = (read_file(file)); |
| 145 | if (!data) { |
| 146 | log_file(INETD_LOG_FILE, "*Error in reading host file: " |
| 147 | + file +"\n\n"); |
| 148 | return 0; |
| 149 | } |
| 150 | |
| 151 | data = old_explode(data, "\n"); |
| 152 | mapping new_hosts = m_allocate(sizeof(data),1); |
| 153 | |
| 154 | foreach(string line: data) { |
| 155 | if (line == "" || line[0] == '#') |
| 156 | continue; |
| 157 | mixed fields = old_explode(line, HOSTFILE_DEL); |
| 158 | if (sizeof(fields) < 5) { |
| 159 | log_file(INETD_LOG_FILE, sprintf( |
| 160 | "*Parse error in hosts file: %s, Line: %s\n", file, line)); |
| 161 | continue; |
| 162 | } |
| 163 | |
| 164 | string name = lower_case(fields[HOST_NAME]); |
| 165 | if (member(new_hosts, name)) |
| 166 | continue; // mud already in list |
| 167 | |
| 168 | string *local_cmds = old_explode(fields[LOCAL_COMMANDS],HOSTFILE_DEL2); |
| 169 | if (member(local_cmds,"*") != -1) |
| 170 | local_cmds = local_cmds - ({ "*" }) + COMMANDS; |
| 171 | |
| 172 | new_hosts[name] = ({ capitalize(fields[HOST_NAME]), |
| 173 | fields[HOST_IP], |
| 174 | to_int(fields[HOST_UDP_PORT]), |
| 175 | local_cmds, |
| 176 | old_explode(fields[HOST_COMMANDS], HOSTFILE_DEL2), |
| 177 | UNKNOWN |
| 178 | }); |
| 179 | /* |
| 180 | * Get existing host status from current active host lost as long as the |
| 181 | * IP and UDP ports are the same. |
| 182 | */ |
| 183 | if (hosts[name] && |
| 184 | hosts[name][HOST_IP] == new_hosts[name][HOST_IP] && |
| 185 | hosts[name][HOST_UDP_PORT] == new_hosts[name][HOST_UDP_PORT]) |
| 186 | new_hosts[name][HOST_STATUS] = hosts[name][HOST_STATUS]; |
| 187 | } |
| 188 | return new_hosts; |
| 189 | } |
| 190 | |
| 191 | /* |
| 192 | * Read the INETD_HOSTS file and set the "hosts" mapping from it. |
| 193 | * Retain existing HOST_STATUS fields. |
| 194 | * Reads INETD_HOSTSS and INETD_HOSTS.dump |
| 195 | * Hosts from INETD_HOSTS are included in the static host list. |
| 196 | */ |
| 197 | void set_host_list() { |
| 198 | |
| 199 | // read the static host file and the last host file dump. |
| 200 | mapping static_hosts = read_host_list(HOST_FILE); |
| 201 | if (!static_hosts) |
| 202 | return; // retain the old list(s) |
| 203 | |
| 204 | // remember the static hosts |
| 205 | static_host_list = m_reallocate(static_hosts,0); |
| 206 | |
| 207 | // read the last host file dump and add the static hosts. Then the static |
| 208 | // hosts have precedence over the ones from the dump. |
| 209 | hosts = (read_host_list(HOST_FILE".dump") || ([])) + static_hosts; |
| 210 | |
| 211 | } |
| 212 | |
| 213 | mixed get_hosts() {return copy(hosts);} |
| 214 | |
| 215 | int dump_hosts_list() { |
| 216 | |
| 217 | write_file(HOST_FILE+".dump", sprintf( |
| 218 | "#Hostlist dump from "MUDNAME", created %s\n", strftime()), 1); |
| 219 | |
| 220 | foreach(string mudname, mixed tmp : hosts) { |
| 221 | // skip muds we did not hear from for 2 days. |
| 222 | // if (tmp[HOST_STATUS] + 172800 < time()) |
| 223 | // continue; |
| 224 | write_file(HOST_FILE+".dump", |
| 225 | sprintf("%s:%s:%d:%s:%s\n",tmp[0],tmp[1],tmp[2],implode(tmp[3],","), |
| 226 | implode(tmp[4],","))); |
| 227 | } |
| 228 | return 1; |
| 229 | } |
| 230 | |
| 231 | /* |
| 232 | * Make a PING request to all muds in the "hosts" mapping to set |
| 233 | * HOST_STATUS information. |
| 234 | * But don't ping all muds at once, because that may overflow the callout |
| 235 | * table during mud startup, when hundreds of objects make callouts. |
| 236 | */ |
| 237 | void startup(string *muds) { |
| 238 | |
| 239 | if (!pointerp(muds)) |
| 240 | muds=m_indices(hosts); |
| 241 | if (!sizeof(muds)) |
| 242 | return; |
| 243 | string *part; |
| 244 | if (sizeof(muds) > 9) |
| 245 | part=muds[0..9]; |
| 246 | else |
| 247 | part=muds; |
| 248 | foreach(string mud: part) |
| 249 | _send_udp(mud, ([ REQUEST: PING ]), 1); |
| 250 | muds -= part; |
| 251 | if (sizeof(muds)) |
| 252 | call_out(#'startup, 4, muds); |
| 253 | } |
| 254 | |
| 255 | /* |
| 256 | * Remove a buffered packet from the "incoming_packets" mapping. |
| 257 | */ |
| 258 | void remove_incoming(string id) { |
| 259 | m_delete(incoming_packets, id); |
| 260 | } |
| 261 | |
| 262 | /* |
| 263 | * Decode a string from a UDP packet. |
| 264 | * Returns: The actual value of the argument (either int or string) |
| 265 | */ |
| 266 | mixed decode(string arg) { |
| 267 | |
| 268 | if (sizeof(arg) && arg[0] == '$') |
| 269 | return arg[1..]; |
| 270 | #ifdef RESTRICTED_CASTS |
| 271 | if (to_string(to_int(arg)) == arg) |
| 272 | return to_int(arg); |
| 273 | #else |
| 274 | if ((string)((int)arg) == arg) |
| 275 | return (int)arg; |
| 276 | #endif |
| 277 | return arg; |
| 278 | } |
| 279 | |
| 280 | /* |
| 281 | * Decode a UDP packet. |
| 282 | * Arguments: UDP packet as a string. |
| 283 | * Returns: The decoded information as a mapping, 1 for succes but no |
| 284 | * output (buffered packet), or 0 on error. |
| 285 | */ |
| 286 | mixed decode_packet(string packet, string delimiter) { |
| 287 | string *data; |
| 288 | mapping ret; |
| 289 | string info, tmp; |
| 290 | mixed class; |
| 291 | int i, id, n; |
| 292 | |
| 293 | /* If this packet has been split, handle buffering. */ |
| 294 | if (packet[0..sizeof(PACKET)] == PACKET + ":") { |
| 295 | if (sscanf(packet, PACKET + ":%s:%d:%d/%d" + delimiter + "%s", |
| 296 | class, id, i, n, tmp) != 5) |
| 297 | return 0; |
| 298 | class = lower_case(class) + ":" + id; |
| 299 | if (pointerp(incoming_packets[class])) { |
| 300 | incoming_packets[class][i-1] = tmp; |
| 301 | if (member(incoming_packets[class], 0) == -1) { |
| 302 | ret = |
| 303 | decode_packet(implode(incoming_packets[class], ""), delimiter); |
| 304 | remove_incoming(class); |
| 305 | return ret; |
| 306 | } |
| 307 | } else { |
| 308 | incoming_packets[class] = allocate(n); |
| 309 | incoming_packets[class][i-1] = tmp; |
| 310 | /* Is it possible to already have a timeout running here ?!? */ |
| 311 | /* If no timeout is running then start one. */ |
| 312 | if (!pending_data[class]) { |
| 313 | call_out("remove_incoming", |
| 314 | REPLY_TIME_OUT + REPLY_TIME_OUT * RETRIES, class); |
| 315 | } else { |
| 316 | DEBUG("\nINETD: *** Buffered packet Timeout already running! ***\n"); |
| 317 | } |
| 318 | } |
| 319 | return 1; |
| 320 | } |
| 321 | ret = ([ ]); |
| 322 | for(i = 0, n = sizeof(data = old_explode(packet, delimiter)); i < n; i++) { |
| 323 | /* DATA fields can be denoted by a preceeding blank field. */ |
| 324 | if (data[i] == "") { |
| 325 | tmp = DATA; |
| 326 | /* Test for illegal packet length (no DATA) */ |
| 327 | if (++i >= n) |
| 328 | return 0; |
| 329 | info = data[i]; |
| 330 | } |
| 331 | else if (sscanf(data[i], "%s:%s", tmp, info) != 2) |
| 332 | return 0; |
| 333 | switch((string)(class = decode(tmp))) { |
| 334 | case DATA: |
| 335 | #ifdef USE_EXTRACT |
| 336 | return ret + ([ DATA: decode(implode( |
| 337 | ({ info }) + extract(data, i+1), delimiter)) ]); |
| 338 | #else |
| 339 | return ret + ([ DATA: decode(implode( |
| 340 | ({ info }) + data[i+1..], delimiter)) ]); |
| 341 | #endif |
| 342 | case SYSTEM: |
| 343 | ret[class] = old_explode(info, ":"); |
| 344 | continue; |
| 345 | default: |
| 346 | ret[class] = decode(info); |
| 347 | continue; |
| 348 | } |
| 349 | } |
| 350 | return ret; |
| 351 | } |
| 352 | |
| 353 | /* Check wether a UDP packet was valid. |
| 354 | * Logs are made and "host" information is updated as appropriate. |
| 355 | * Arguments: Decoded UDP packet (mapping) |
| 356 | * Returns: 0 for valid packets, an error string otherwise. |
| 357 | */ |
| 358 | string valid_request(mapping data) { |
| 359 | mixed host_data; |
| 360 | string *muds; |
| 361 | string req; |
| 362 | int i; |
| 363 | |
| 364 | if (!data[NAME] || !data[UDP_PORT]) |
| 365 | return DATE + ": Illegal packet.\n"; |
| 366 | if (host_data = hosts[lower_case(data[NAME])]) { |
| 367 | if (data[HOST] != host_data[HOST_IP]) { |
| 368 | if (data[NAME] == LOCAL_NAME) |
| 369 | return DATE + ": *** FAKE MUD ***\n"; |
| 370 | log_file(INETD_LOG_FILE, DATE + ": Host change:\n" + |
| 371 | host_data[HOST_NAME] + ": " + host_data[HOST_IP] + " -> " + |
| 372 | data[HOST] + "\n\n"); |
| 373 | host_data[HOST_IP] = data[HOST]; |
| 374 | } |
| 375 | if (data[UDP_PORT] != host_data[HOST_UDP_PORT]) { |
| 376 | if (data[NAME] == LOCAL_NAME) |
| 377 | return DATE + ": *** FAKE MUD ***\n"; |
| 378 | log_file(INETD_LOG_FILE, DATE + ": Port change:\n" + |
| 379 | host_data[HOST_NAME] + " (" + host_data[HOST_IP] + "): " + |
| 380 | host_data[HOST_UDP_PORT] + " -> " + data[UDP_PORT] + "\n\n"); |
| 381 | host_data[HOST_UDP_PORT] = data[UDP_PORT]; |
| 382 | } |
| 383 | } else { |
| 384 | if (lower_case(data[NAME]) == lower_case(LOCAL_NAME)) |
| 385 | return DATE + ": *** FAKE MUD ***\n"; |
| 386 | for(i = sizeof(muds = m_indices(hosts)); i--; ) { |
| 387 | host_data = hosts[muds[i]]; |
| 388 | if (data[HOST] == host_data[HOST_IP] && |
| 389 | data[UDP_PORT] == host_data[HOST_UDP_PORT]) { |
| 390 | log_file(INETD_LOG_FILE, DATE + ": Name change:\n" + |
| 391 | host_data[HOST_NAME] + " (" + host_data[HOST_IP] + |
| 392 | ") -> " + data[NAME] + "\n\n"); |
| 393 | host_data[HOST_NAME] = data[NAME]; |
| 394 | hosts[lower_case(data[NAME])] = host_data; |
| 395 | m_delete(hosts, muds[i]); |
| 396 | i = -2; |
| 397 | break; |
| 398 | } |
| 399 | } |
| 400 | if (i != -2) { |
| 401 | host_data = hosts[lower_case(data[NAME])] = ({ |
| 402 | data[NAME], |
| 403 | data[HOST], |
| 404 | data[UDP_PORT], |
| 405 | COMMANDS, |
| 406 | ({ "*" }), |
| 407 | UP |
| 408 | }); |
| 409 | log_file(INETD_LOG_FILE, DATE + ": New mud.\n" + data[NAME] + ":" + |
| 410 | data[HOST] + ":" + data[UDP_PORT] + "\n\n"); |
| 411 | } |
| 412 | } |
| 413 | if (!(req = data[REQUEST])) |
| 414 | return DATE + ": System message.\n"; |
| 415 | if (req != PING && |
| 416 | req != QUERY && |
| 417 | req != REPLY && |
| 418 | member(host_data[LOCAL_COMMANDS], req) == -1) |
| 419 | { |
| 420 | /* This should probably send a system message too. */ |
| 421 | _send_udp(host_data[HOST_NAME], ([ |
| 422 | REQUEST: REPLY, |
| 423 | RECIPIENT: data[SENDER], |
| 424 | ID: data[ID], |
| 425 | DATA: "Invalid request @" + LOCAL_NAME + ": " + |
| 426 | capitalize(data[REQUEST]) + "\n" |
| 427 | ]) ); |
| 428 | return DATE + ": Invalid request.\n"; |
| 429 | } |
| 430 | return 0; |
| 431 | } |
| 432 | |
| 433 | /* |
| 434 | * Incoming UDP packets are sent to this function to be interpretted. |
| 435 | * The packet is decoded, checked for validity, HOST_STATUS is updated |
| 436 | * and the appropriate udp module called. |
| 437 | * Arguments: Senders IP address (string) |
| 438 | * UDP packet (string) |
| 439 | */ |
| 440 | void _receive_udp(string sender, string packet) { |
| 441 | mapping data; |
| 442 | string req, err, id; |
| 443 | |
| 444 | #ifdef UDP_MASTER |
| 445 | if (!previous_object() || |
| 446 | object_name(previous_object()) != UDP_MASTER) { |
| 447 | log_file(INETD_LOG_FILE, DATE + ": Illegal call of _receive_udp() by " + |
| 448 | object_name(previous_object()) + "\n\n"); |
| 449 | return; |
| 450 | } |
| 451 | #endif |
| 452 | |
| 453 | ZDEBUG(sprintf("%O -> %.500O\n",sender, packet)); |
| 454 | if ( |
| 455 | #ifdef DELIMITER_COMPAT |
| 456 | #ifdef USE_EXTRACT |
| 457 | (!(data = decode_packet(packet, NEW_DELIMITER)) |
| 458 | #else |
| 459 | (!(data = decode_packet(packet, NEW_DELIMITER)) |
| 460 | #endif |
| 461 | || (data[HOST] = sender) && (err = valid_request(data))) && |
| 462 | (!(data = decode_packet(packet, OLD_DELIMITER)) || |
| 463 | (data[HOST] = sender) && (err = valid_request(data))) |
| 464 | #else |
| 465 | #ifdef USE_EXTRACT |
| 466 | !(data = decode_packet(packet, DELIMITER)) |
| 467 | #else |
| 468 | !(data = decode_packet(packet, DELIMITER)) |
| 469 | #endif |
| 470 | #endif |
| 471 | ) { |
| 472 | if (!data) |
| 473 | #ifdef RECEIVE_UDP_COMPAT |
| 474 | RECEIVE_UDP_COMPAT(sender, packet); |
| 475 | #else |
| 476 | log_file(INETD_LOG_FILE, DATE + ": Received invalid packet.\nSender: " + |
| 477 | sender + "\nPacket:\n" + packet + "\n\n"); |
| 478 | #endif |
| 479 | return; |
| 480 | } |
| 481 | #ifdef DELIMITER_COMPAT |
| 482 | if (!mappingp(data)) |
| 483 | return; |
| 484 | if (err) |
| 485 | #else |
| 486 | if (!mappingp(data)) |
| 487 | return; |
| 488 | data[HOST] = sender; |
| 489 | if (err = valid_request(data)) |
| 490 | #endif |
| 491 | { |
| 492 | log_file(INETD_LOG_FILE, err + "Sender: " + sender + "\nPacket:\n" + |
| 493 | packet + "\n\n"); |
| 494 | return; |
| 495 | } |
| 496 | hosts[lower_case(data[NAME])][HOST_STATUS] = UP; |
| 497 | if ((req = data[REQUEST]) == REPLY) { |
| 498 | mapping pending; |
| 499 | |
| 500 | /* If we can't find the reply in the pending list then bin it. */ |
| 501 | if (!(pending = pending_data[lower_case(data[NAME]) + ":" + data[ID]])) |
| 502 | return; |
| 503 | /* Set data[REQUEST] correctly, but still send to (req = REPLY) */ |
| 504 | data[REQUEST] = pending[REQUEST]; |
| 505 | #ifdef INETD_DIAGNOSTICS |
| 506 | data[PACKET_LOSS] = pending[PACKET_LOSS]; |
| 507 | data[RESPONSE_TIME] = time() - pending[RESPONSE_TIME] + 1; |
| 508 | #endif |
| 509 | #if 0 |
| 510 | /* channel replies may not include a recipient, and shouldn't have one set */ |
| 511 | /* Restore the RECIPIENT in replies if none given and it is known. */ |
| 512 | if (!data[RECIPIENT] && pending[SENDER]) |
| 513 | data[RECIPIENT] = pending[SENDER]; |
| 514 | #endif |
| 515 | m_delete(pending_data, lower_case(data[NAME]) + ":" + data[ID]); |
| 516 | } |
| 517 | else if (data[ID]) { |
| 518 | id = lower_case(data[NAME]) + ":" + data[ID]; |
| 519 | if (member(received_ids, id) == -1) |
| 520 | { |
| 521 | received_ids += ({ id }); |
| 522 | call_out("remove_received_id", |
| 523 | REPLY_TIME_OUT + REPLY_TIME_OUT * (RETRIES + 1), id); |
| 524 | } |
| 525 | else |
| 526 | add_system_field(data, REPEAT); |
| 527 | } |
| 528 | if (err = catch( |
| 529 | call_other(UDP_CMD_DIR + req, "udp_" + req, deep_copy(data));publish)) |
| 530 | { |
| 531 | _send_udp(data[NAME], ([ |
| 532 | REQUEST: REPLY, |
| 533 | RECIPIENT: data[SENDER], |
| 534 | ID: data[ID], |
| 535 | DATA: capitalize(req)+ " request failed @" + LOCAL_NAME + ".\n" |
| 536 | ]) ); |
| 537 | log_file(INETD_LOG_FILE, DATE + ": " + data[REQUEST] + " from " + |
| 538 | data[NAME] + " failed.\n" + err + packet + "\n\n"); |
| 539 | } |
| 540 | } |
| 541 | |
| 542 | int do_match(string mudname, string match_str) { |
| 543 | return mudname[0..sizeof(match_str)-1] == match_str; |
| 544 | } |
| 545 | |
| 546 | #ifdef NO_CLOSURES |
| 547 | |
| 548 | status greater(mixed a, mixed b) { |
| 549 | return a > b; |
| 550 | } |
| 551 | |
| 552 | string *expand_mud_name(string name) { |
| 553 | return sort_array( |
| 554 | filter(m_indices(hosts), "do_match", this_object(), name), |
| 555 | "greater", |
| 556 | this_object() |
| 557 | ); |
| 558 | } |
| 559 | |
| 560 | #else |
| 561 | |
| 562 | string *expand_mud_name(string name) { |
| 563 | return sort_array( |
| 564 | filter(m_indices(hosts), #'do_match, name), |
| 565 | #'> |
| 566 | ); |
| 567 | } |
| 568 | |
| 569 | #endif |
| 570 | |
| 571 | string encode(mixed arg) { |
| 572 | if (objectp(arg)) |
| 573 | return object_name(arg); |
| 574 | if (stringp(arg) && sizeof(arg) && |
| 575 | (arg[0] == '$' || |
| 576 | #ifdef RESTRICTED_CASTS |
| 577 | to_string(to_int(arg)) == (string)arg)) |
| 578 | #else |
| 579 | (string)to_int(arg) == (string)arg)) |
| 580 | #endif |
| 581 | return "$" + arg; |
| 582 | return to_string(arg); |
| 583 | } |
| 584 | |
| 585 | string encode_packet(mapping data) { |
| 586 | int i; |
| 587 | mixed indices; |
| 588 | string header, body, t1, t2; |
| 589 | string *ret; |
| 590 | status data_flag; |
| 591 | |
| 592 | for(ret = ({ }), i = sizeof(indices = m_indices(data)); i--; ) { |
| 593 | if (indices[i] == DATA) { |
| 594 | data_flag = 1; |
| 595 | continue; |
| 596 | } |
| 597 | header = encode(indices[i]); |
| 598 | body = encode(data[indices[i]]); |
| 599 | if (sscanf(header, "%s:%s", t1, t2) || |
| 600 | sscanf(header + body, "%s" + DELIMITER + "%s", t1, t2) |
| 601 | ) |
| 602 | return 0; |
| 603 | |
| 604 | ret += ({ header + ":" + body }); |
| 605 | } |
| 606 | if (data_flag) |
| 607 | #ifdef USE_OLD_DATA_FORMAT |
| 608 | ret += ({ DATA + ":" + encode(data[DATA]) }); |
| 609 | #else |
| 610 | ret += ({ "", encode(data[DATA]) }); |
| 611 | #endif |
| 612 | return implode(ret, DELIMITER); |
| 613 | } |
| 614 | |
| 615 | // Funktion explode_ |
| 616 | // Die Funktion zerlegt den String packet in gleich lange Teilstrings |
| 617 | // der Laenge len und gibt die Teilstrings als Array zurueck. Der letzte |
| 618 | // Teilstring kann kuerzer sein als die anderen. |
| 619 | string *explode_packet(string packet, int len) { |
| 620 | |
| 621 | int ptr, m_ptr,size; |
| 622 | string *result; |
| 623 | |
| 624 | // Variablen initialisieren |
| 625 | m_ptr=ptr=0; |
| 626 | size=sizeof(packet); |
| 627 | result=({}); |
| 628 | |
| 629 | // Um Arrayadditionen zu vermeiden wird vorher allokiert. Die Division |
| 630 | // durch 0 ist nicht abgefangen, da bei len=0 was im Aufruf falch ist. |
| 631 | result=allocate((size/len+1)); |
| 632 | |
| 633 | while (ptr<size) { |
| 634 | result[m_ptr] = // Aktuellen Teilstring speichern |
| 635 | #ifdef USE_EXTRACT |
| 636 | extract(packet,ptr,ptr+len-1); |
| 637 | #else |
| 638 | packet[ptr..ptr+len-1]; |
| 639 | #endif |
| 640 | ptr+=len;// Neuen Pointer im String berechnen |
| 641 | m_ptr++; // Neuen Pointer im Mapping berechnen. Hier nutze ich eine |
| 642 | // Variable mehr als noetig, weil das billiger ist, als jedes |
| 643 | // mal ptr=m_ptr*len auszurechnen. Lieber zwei Additionen als |
| 644 | // eine Multiplikation und eine Addtion. |
| 645 | } |
| 646 | |
| 647 | // Wenn size/len aufgeht, ist das Result-Feld zu gross. Dann bleibt |
| 648 | // ein Element leer, das wird hier gestrippt. Das ist billiger als |
| 649 | // jedesmal auszurechnen, ob size/len aufgeht. |
| 650 | return result-({0}); |
| 651 | |
| 652 | } |
| 653 | |
| 654 | varargs string _send_udp(string mudname, mapping data, int expect_reply) { |
| 655 | mixed host_data; |
| 656 | string *packet_arr; |
| 657 | string packet; |
| 658 | int i; |
| 659 | |
| 660 | mudname = lower_case(mudname); |
| 661 | if (!(host_data = hosts[mudname])) { |
| 662 | string *names; |
| 663 | |
| 664 | if (sizeof(names = expand_mud_name(mudname)) == 1) |
| 665 | host_data = hosts[mudname = names[0]]; |
| 666 | else |
| 667 | #ifdef SEND_UDP_COMPAT |
| 668 | return (string)SEND_UDP_COMPAT(mudname, data, expect_reply); |
| 669 | #else |
| 670 | if (!sizeof(names)) |
| 671 | return "Unbekannter Mudname: " + capitalize(mudname) + "\n"; |
| 672 | else |
| 673 | return break_string("Mudname ("+capitalize(mudname)+ |
| 674 | ") nicht eindeutig, es passen: "+implode(map(names,#'capitalize),", ")+ |
| 675 | ".\n",78); |
| 676 | #endif |
| 677 | } |
| 678 | |
| 679 | if (data[REQUEST] != PING && |
| 680 | data[REQUEST] != QUERY && |
| 681 | data[REQUEST] != REPLY && |
| 682 | member(host_data[HOST_COMMANDS], "*") == -1 && |
| 683 | member(host_data[HOST_COMMANDS], data[REQUEST]) == -1) |
| 684 | return capitalize(data[REQUEST]) + ": Command unavailable @" + |
| 685 | host_data[HOST_NAME] + "\n"; |
| 686 | |
| 687 | data[NAME] = LOCAL_NAME; |
| 688 | data[UDP_PORT] = LOCAL_UDP_PORT; |
| 689 | |
| 690 | if (expect_reply) { |
| 691 | /* Don't use zero. */ |
| 692 | data[ID] = ++packet_id; |
| 693 | /* Don't need deep_copy() as we are changing the mapping size. */ |
| 694 | pending_data[mudname + ":" + packet_id] = |
| 695 | #ifdef INETD_DIAGNOSTICS |
| 696 | data + ([ NAME: host_data[HOST_NAME], RESPONSE_TIME: time() ]); |
| 697 | #else |
| 698 | data + ([ NAME: host_data[HOST_NAME] ]); |
| 699 | #endif |
| 700 | } |
| 701 | if (!(packet = encode_packet(data))) { |
| 702 | if (expect_reply) |
| 703 | pending_data = m_copy_delete(pending_data, mudname + ":" + packet_id); |
| 704 | log_file(INETD_LOG_FILE, DATE + ": Illegal packet sent by " + |
| 705 | object_name(previous_object()) + "\n\n"); |
| 706 | return "inetd: Illegal packet.\n"; |
| 707 | } |
| 708 | if (expect_reply) |
| 709 | call_out("reply_time_out", REPLY_TIME_OUT, mudname + ":" + packet_id); |
| 710 | |
| 711 | if (sizeof(packet) <= MAX_PACKET_LEN) |
| 712 | packet_arr = ({ packet }); |
| 713 | else { |
| 714 | string header; |
| 715 | int max; |
| 716 | |
| 717 | /* Be careful with the ID. data[ID] could have been set up by RETRY */ |
| 718 | header = |
| 719 | PACKET + ":" + lower_case(LOCAL_NAME) + ":" + |
| 720 | ((expect_reply || data[REQUEST] != REPLY)&& data[ID] ? |
| 721 | data[ID] : ++packet_id) + ":"; |
| 722 | |
| 723 | /* Allow 8 extra chars: 3 digits + "/" + 3 digits + DELIMITER */ |
| 724 | packet_arr = explode_packet(packet, |
| 725 | MAX_PACKET_LEN - (sizeof(header) + 8)); |
| 726 | |
| 727 | for(i = max = sizeof(packet_arr); i--; ) |
| 728 | packet_arr[i] = |
| 729 | header + (i+1) + "/" + max + DELIMITER + packet_arr[i]; |
| 730 | } |
| 731 | |
| 732 | for(i = sizeof(packet_arr); i--; ) |
| 733 | { |
| 734 | ZDEBUG(sprintf("%O <- %.500O\n",host_data[HOST_IP], packet_arr[i])); |
| 735 | if (!send_udp( |
| 736 | host_data[HOST_IP], host_data[HOST_UDP_PORT], packet_arr[i])) |
| 737 | return "inetd: Error in sending packet.\n"; |
| 738 | } |
| 739 | return 0; |
| 740 | } |
| 741 | |
| 742 | void reply_time_out(string id) { |
| 743 | mapping data; |
| 744 | |
| 745 | if (data = pending_data[id]) { |
| 746 | object ob; |
| 747 | |
| 748 | #ifdef INETD_DIAGNOSTICS |
| 749 | data[PACKET_LOSS]++; |
| 750 | #endif |
| 751 | if (data[RETRY] < RETRIES) { |
| 752 | mapping send; |
| 753 | |
| 754 | data[RETRY]++; |
| 755 | /* We must use a copy so the NAME field in pending_data[id] |
| 756 | * isn't corrupted by _send_udp(). */ |
| 757 | send = deep_copy(data); |
| 758 | send = m_copy_delete(send, RETRY); |
| 759 | #ifdef INETD_DIAGNOSTICS |
| 760 | send = m_copy_delete(send, PACKET_LOSS); |
| 761 | send = m_copy_delete(send, RESPONSE_TIME); |
| 762 | #endif |
| 763 | call_out("reply_time_out", REPLY_TIME_OUT, id); |
| 764 | _send_udp(data[NAME], send); |
| 765 | return; |
| 766 | } |
| 767 | data = m_copy_delete(data, RETRY); |
| 768 | #ifdef INETD_DIAGNOSTICS |
| 769 | data = m_copy_delete(data, RESPONSE_TIME); |
| 770 | #endif |
| 771 | catch(call_other(UDP_CMD_DIR + REPLY, "udp_" + REPLY, |
| 772 | add_system_field(data, TIME_OUT));publish); |
| 773 | /* It's just possible this was removed from the host list. */ |
| 774 | if (hosts[lower_case(data[NAME])]) |
| 775 | hosts[lower_case(data[NAME])][HOST_STATUS] = DOWN; |
| 776 | remove_incoming(lower_case(data[NAME]) + ":" + id); |
| 777 | } |
| 778 | pending_data = m_copy_delete(pending_data, id); |
| 779 | } |
| 780 | |
| 781 | void remove_received_id(string id) { |
| 782 | received_ids -= ({ id }); |
| 783 | } |
| 784 | |
| 785 | varargs mixed query(string what, mixed extra1, mixed extra2) { |
| 786 | mixed data; |
| 787 | |
| 788 | switch(what) { |
| 789 | case "commands": |
| 790 | return COMMANDS; |
| 791 | case "hosts": |
| 792 | return deep_copy(hosts); |
| 793 | case "pending": |
| 794 | return deep_copy(pending_data); |
| 795 | case "incoming": |
| 796 | return deep_copy(incoming_packets); |
| 797 | case "received": |
| 798 | return ({ }) + received_ids; |
| 799 | /* args: "valid_request", request, mudname */ |
| 800 | case "valid_request": |
| 801 | if (data = hosts[extra2]) |
| 802 | return member(data[HOST_COMMANDS], "*") != -1 || |
| 803 | member(data[HOST_COMMANDS], extra1) != -1; |
| 804 | return 0; |
| 805 | } |
| 806 | return(0); |
| 807 | } |
| 808 | |