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