blob: a13c91bbbfa0af6d6d83b72eac24a558bb48a1dd [file] [log] [blame]
MG Mud User88f12472016-06-24 23:31:02 +02001/*
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
Zesstra66e50d22016-07-07 00:05:08 +020016/*
MG Mud User88f12472016-06-24 23:31:02 +020017#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())
Zesstra66e50d22016-07-07 00:05:08 +020021*/
22#define ZDEBUG(x)
MG Mud User88f12472016-06-24 23:31:02 +020023
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
92private nosave mapping hosts, pending_data, incoming_packets;
93private nosave mapping static_host_list;
94private nosave string *received_ids;
95private nosave int packet_id;
96
97void set_host_list();
98varargs string _send_udp(string mudname, mapping data, int expect_reply);
99
100#define DEBUG(msg)
101
102void 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
117CREATE_FUN() {
118#elif !defined(COMPAT_FLAG) || defined(ZEBEDEE)
119void create() {
120#else
121void reset(arg) {
122 if (arg)
123 return;
124#endif
125 my_create();
126}
127
128status check_system_field(mapping data, string field) {
129 return data[SYSTEM] && member(data[SYSTEM], field) != -1;
130}
131
132mapping 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
142private 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 */
197void 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
213mixed get_hosts() {return copy(hosts);}
214
215int 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 */
237void 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 */
258void 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 */
Zesstraac4c5632019-09-30 23:03:04 +0200266string|int decode(string arg)
267{
MG Mud User88f12472016-06-24 23:31:02 +0200268 if (sizeof(arg) && arg[0] == '$')
269 return arg[1..];
Zesstraac4c5632019-09-30 23:03:04 +0200270 else if ((arg & "0123456789") == arg)
MG Mud User88f12472016-06-24 23:31:02 +0200271 return to_int(arg);
Zesstraac4c5632019-09-30 23:03:04 +0200272
MG Mud User88f12472016-06-24 23:31:02 +0200273 return arg;
274}
275
276/*
277 * Decode a UDP packet.
278 * Arguments: UDP packet as a string.
279 * Returns: The decoded information as a mapping, 1 for succes but no
280 * output (buffered packet), or 0 on error.
281 */
282mixed decode_packet(string packet, string delimiter) {
283 string *data;
284 mapping ret;
285 string info, tmp;
286 mixed class;
287 int i, id, n;
288
289 /* If this packet has been split, handle buffering. */
290 if (packet[0..sizeof(PACKET)] == PACKET + ":") {
291 if (sscanf(packet, PACKET + ":%s:%d:%d/%d" + delimiter + "%s",
292 class, id, i, n, tmp) != 5)
293 return 0;
294 class = lower_case(class) + ":" + id;
295 if (pointerp(incoming_packets[class])) {
296 incoming_packets[class][i-1] = tmp;
297 if (member(incoming_packets[class], 0) == -1) {
298 ret =
299 decode_packet(implode(incoming_packets[class], ""), delimiter);
300 remove_incoming(class);
301 return ret;
302 }
303 } else {
304 incoming_packets[class] = allocate(n);
305 incoming_packets[class][i-1] = tmp;
306/* Is it possible to already have a timeout running here ?!? */
307 /* If no timeout is running then start one. */
308 if (!pending_data[class]) {
309 call_out("remove_incoming",
310 REPLY_TIME_OUT + REPLY_TIME_OUT * RETRIES, class);
311 } else {
312 DEBUG("\nINETD: *** Buffered packet Timeout already running! ***\n");
313 }
314 }
315 return 1;
316 }
317 ret = ([ ]);
318 for(i = 0, n = sizeof(data = old_explode(packet, delimiter)); i < n; i++) {
319 /* DATA fields can be denoted by a preceeding blank field. */
320 if (data[i] == "") {
321 tmp = DATA;
322 /* Test for illegal packet length (no DATA) */
323 if (++i >= n)
324 return 0;
325 info = data[i];
326 }
327 else if (sscanf(data[i], "%s:%s", tmp, info) != 2)
328 return 0;
329 switch((string)(class = decode(tmp))) {
330 case DATA:
331#ifdef USE_EXTRACT
332 return ret + ([ DATA: decode(implode(
333 ({ info }) + extract(data, i+1), delimiter)) ]);
334#else
335 return ret + ([ DATA: decode(implode(
336 ({ info }) + data[i+1..], delimiter)) ]);
337#endif
338 case SYSTEM:
339 ret[class] = old_explode(info, ":");
340 continue;
341 default:
342 ret[class] = decode(info);
343 continue;
344 }
345 }
346 return ret;
347}
348
349/* Check wether a UDP packet was valid.
350 * Logs are made and "host" information is updated as appropriate.
351 * Arguments: Decoded UDP packet (mapping)
352 * Returns: 0 for valid packets, an error string otherwise.
353 */
354string valid_request(mapping data) {
355 mixed host_data;
356 string *muds;
357 string req;
358 int i;
359
360 if (!data[NAME] || !data[UDP_PORT])
361 return DATE + ": Illegal packet.\n";
362 if (host_data = hosts[lower_case(data[NAME])]) {
363 if (data[HOST] != host_data[HOST_IP]) {
364 if (data[NAME] == LOCAL_NAME)
365 return DATE + ": *** FAKE MUD ***\n";
366 log_file(INETD_LOG_FILE, DATE + ": Host change:\n" +
367 host_data[HOST_NAME] + ": " + host_data[HOST_IP] + " -> " +
368 data[HOST] + "\n\n");
369 host_data[HOST_IP] = data[HOST];
370 }
371 if (data[UDP_PORT] != host_data[HOST_UDP_PORT]) {
372 if (data[NAME] == LOCAL_NAME)
373 return DATE + ": *** FAKE MUD ***\n";
374 log_file(INETD_LOG_FILE, DATE + ": Port change:\n" +
375 host_data[HOST_NAME] + " (" + host_data[HOST_IP] + "): " +
376 host_data[HOST_UDP_PORT] + " -> " + data[UDP_PORT] + "\n\n");
377 host_data[HOST_UDP_PORT] = data[UDP_PORT];
378 }
379 } else {
380 if (lower_case(data[NAME]) == lower_case(LOCAL_NAME))
381 return DATE + ": *** FAKE MUD ***\n";
382 for(i = sizeof(muds = m_indices(hosts)); i--; ) {
383 host_data = hosts[muds[i]];
384 if (data[HOST] == host_data[HOST_IP] &&
385 data[UDP_PORT] == host_data[HOST_UDP_PORT]) {
386 log_file(INETD_LOG_FILE, DATE + ": Name change:\n" +
387 host_data[HOST_NAME] + " (" + host_data[HOST_IP] +
388 ") -> " + data[NAME] + "\n\n");
389 host_data[HOST_NAME] = data[NAME];
390 hosts[lower_case(data[NAME])] = host_data;
391 m_delete(hosts, muds[i]);
392 i = -2;
393 break;
394 }
395 }
396 if (i != -2) {
397 host_data = hosts[lower_case(data[NAME])] = ({
398 data[NAME],
399 data[HOST],
400 data[UDP_PORT],
401 COMMANDS,
402 ({ "*" }),
403 UP
404 });
405 log_file(INETD_LOG_FILE, DATE + ": New mud.\n" + data[NAME] + ":" +
406 data[HOST] + ":" + data[UDP_PORT] + "\n\n");
407 }
408 }
409 if (!(req = data[REQUEST]))
410 return DATE + ": System message.\n";
411 if (req != PING &&
412 req != QUERY &&
413 req != REPLY &&
414 member(host_data[LOCAL_COMMANDS], req) == -1)
415 {
416 /* This should probably send a system message too. */
417 _send_udp(host_data[HOST_NAME], ([
418 REQUEST: REPLY,
419 RECIPIENT: data[SENDER],
420 ID: data[ID],
421 DATA: "Invalid request @" + LOCAL_NAME + ": " +
422 capitalize(data[REQUEST]) + "\n"
423 ]) );
424 return DATE + ": Invalid request.\n";
425 }
426 return 0;
427}
428
429/*
430 * Incoming UDP packets are sent to this function to be interpretted.
431 * The packet is decoded, checked for validity, HOST_STATUS is updated
432 * and the appropriate udp module called.
433 * Arguments: Senders IP address (string)
434 * UDP packet (string)
435 */
436void _receive_udp(string sender, string packet) {
437 mapping data;
438 string req, err, id;
439
440#ifdef UDP_MASTER
441 if (!previous_object() ||
442 object_name(previous_object()) != UDP_MASTER) {
443 log_file(INETD_LOG_FILE, DATE + ": Illegal call of _receive_udp() by " +
444 object_name(previous_object()) + "\n\n");
445 return;
446 }
447#endif
448
449 ZDEBUG(sprintf("%O -> %.500O\n",sender, packet));
450 if (
451#ifdef DELIMITER_COMPAT
452#ifdef USE_EXTRACT
453 (!(data = decode_packet(packet, NEW_DELIMITER))
454#else
455 (!(data = decode_packet(packet, NEW_DELIMITER))
456#endif
457 || (data[HOST] = sender) && (err = valid_request(data))) &&
458 (!(data = decode_packet(packet, OLD_DELIMITER)) ||
459 (data[HOST] = sender) && (err = valid_request(data)))
460#else
461#ifdef USE_EXTRACT
462 !(data = decode_packet(packet, DELIMITER))
463#else
464 !(data = decode_packet(packet, DELIMITER))
465#endif
466#endif
467 ) {
468 if (!data)
469#ifdef RECEIVE_UDP_COMPAT
470 RECEIVE_UDP_COMPAT(sender, packet);
471#else
472 log_file(INETD_LOG_FILE, DATE + ": Received invalid packet.\nSender: " +
473 sender + "\nPacket:\n" + packet + "\n\n");
474#endif
475 return;
476 }
477#ifdef DELIMITER_COMPAT
478 if (!mappingp(data))
479 return;
480 if (err)
481#else
482 if (!mappingp(data))
483 return;
484 data[HOST] = sender;
485 if (err = valid_request(data))
486#endif
487 {
488 log_file(INETD_LOG_FILE, err + "Sender: " + sender + "\nPacket:\n" +
489 packet + "\n\n");
490 return;
491 }
492 hosts[lower_case(data[NAME])][HOST_STATUS] = UP;
493 if ((req = data[REQUEST]) == REPLY) {
494 mapping pending;
495
496 /* If we can't find the reply in the pending list then bin it. */
497 if (!(pending = pending_data[lower_case(data[NAME]) + ":" + data[ID]]))
498 return;
499 /* Set data[REQUEST] correctly, but still send to (req = REPLY) */
500 data[REQUEST] = pending[REQUEST];
501#ifdef INETD_DIAGNOSTICS
502 data[PACKET_LOSS] = pending[PACKET_LOSS];
503 data[RESPONSE_TIME] = time() - pending[RESPONSE_TIME] + 1;
504#endif
505#if 0
506/* channel replies may not include a recipient, and shouldn't have one set */
507 /* Restore the RECIPIENT in replies if none given and it is known. */
508 if (!data[RECIPIENT] && pending[SENDER])
509 data[RECIPIENT] = pending[SENDER];
510#endif
511 m_delete(pending_data, lower_case(data[NAME]) + ":" + data[ID]);
512 }
513 else if (data[ID]) {
514 id = lower_case(data[NAME]) + ":" + data[ID];
515 if (member(received_ids, id) == -1)
516 {
517 received_ids += ({ id });
518 call_out("remove_received_id",
519 REPLY_TIME_OUT + REPLY_TIME_OUT * (RETRIES + 1), id);
520 }
521 else
522 add_system_field(data, REPEAT);
523 }
524 if (err = catch(
525 call_other(UDP_CMD_DIR + req, "udp_" + req, deep_copy(data));publish))
526 {
527 _send_udp(data[NAME], ([
528 REQUEST: REPLY,
529 RECIPIENT: data[SENDER],
530 ID: data[ID],
531 DATA: capitalize(req)+ " request failed @" + LOCAL_NAME + ".\n"
532 ]) );
533 log_file(INETD_LOG_FILE, DATE + ": " + data[REQUEST] + " from " +
534 data[NAME] + " failed.\n" + err + packet + "\n\n");
535 }
536}
537
538int do_match(string mudname, string match_str) {
539 return mudname[0..sizeof(match_str)-1] == match_str;
540}
541
542#ifdef NO_CLOSURES
543
544status greater(mixed a, mixed b) {
545 return a > b;
546}
547
548string *expand_mud_name(string name) {
549 return sort_array(
550 filter(m_indices(hosts), "do_match", this_object(), name),
551 "greater",
552 this_object()
553 );
554}
555
556#else
557
558string *expand_mud_name(string name) {
559 return sort_array(
560 filter(m_indices(hosts), #'do_match, name),
561 #'>
562 );
563}
564
565#endif
566
567string encode(mixed arg) {
Zesstraac4c5632019-09-30 23:03:04 +0200568 // Objektnamen mit nicht-ASCII hier ist in allen Faellen doof, wenn sie
569 // koennen auch mit Transliteration nicht mehr verlustfrei zurueck
570 // konvertiert werden...
MG Mud User88f12472016-06-24 23:31:02 +0200571 if (objectp(arg))
Zesstraac4c5632019-09-30 23:03:04 +0200572 arg = object_name(arg);
Zesstra3af39ae2019-11-28 23:03:24 +0100573 else if (intp(arg))
Zesstraac4c5632019-09-30 23:03:04 +0200574 arg = to_string(arg);
Zesstra3af39ae2019-11-28 23:03:24 +0100575 else
576 {
577 if (!stringp(arg))
578 arg = to_string(arg);
579 // faengt es mit $ an oder ist es ne zahl (als string, d.h. enthaelt nur
580 // 0-9)? Dann muss nen $ davor.
581 // TODO: Mhmm. Oder vielleicht doch per RegExp?
582 if ( (arg[0] == '$') || ((arg & "0123456789") == arg))
Zesstrab4782c62019-11-28 23:05:53 +0100583 arg = "$" + arg;
Zesstra3af39ae2019-11-28 23:03:24 +0100584 }
Zesstraac4c5632019-09-30 23:03:04 +0200585 return arg;
MG Mud User88f12472016-06-24 23:31:02 +0200586}
587
588string encode_packet(mapping data) {
589 int i;
590 mixed indices;
591 string header, body, t1, t2;
592 string *ret;
593 status data_flag;
594
595 for(ret = ({ }), i = sizeof(indices = m_indices(data)); i--; ) {
596 if (indices[i] == DATA) {
597 data_flag = 1;
598 continue;
599 }
600 header = encode(indices[i]);
601 body = encode(data[indices[i]]);
Zesstra2230e282019-09-30 23:05:02 +0200602 // Eigentlich sollte encode auch die Wandlung in bytes uebernehmen und
603 // dabei nach ASCII wandeln und ggf. transliterieren. Aber sscanf
604 // braucht wieder strings, daher muss es spaeter passieren (direkt vor
605 // Senden).
MG Mud User88f12472016-06-24 23:31:02 +0200606 if (sscanf(header, "%s:%s", t1, t2) ||
607 sscanf(header + body, "%s" + DELIMITER + "%s", t1, t2)
608 )
609 return 0;
Zesstra2230e282019-09-30 23:05:02 +0200610
MG Mud User88f12472016-06-24 23:31:02 +0200611 ret += ({ header + ":" + body });
612 }
613 if (data_flag)
614#ifdef USE_OLD_DATA_FORMAT
615 ret += ({ DATA + ":" + encode(data[DATA]) });
616#else
617 ret += ({ "", encode(data[DATA]) });
618#endif
619 return implode(ret, DELIMITER);
620}
621
622// Funktion explode_
623// Die Funktion zerlegt den String packet in gleich lange Teilstrings
624// der Laenge len und gibt die Teilstrings als Array zurueck. Der letzte
625// Teilstring kann kuerzer sein als die anderen.
626string *explode_packet(string packet, int len) {
627
628 int ptr, m_ptr,size;
629 string *result;
630
631 // Variablen initialisieren
632 m_ptr=ptr=0;
633 size=sizeof(packet);
634 result=({});
635
636 // Um Arrayadditionen zu vermeiden wird vorher allokiert. Die Division
637 // durch 0 ist nicht abgefangen, da bei len=0 was im Aufruf falch ist.
638 result=allocate((size/len+1));
639
640 while (ptr<size) {
641 result[m_ptr] = // Aktuellen Teilstring speichern
642#ifdef USE_EXTRACT
643 extract(packet,ptr,ptr+len-1);
644#else
645 packet[ptr..ptr+len-1];
646#endif
647 ptr+=len;// Neuen Pointer im String berechnen
648 m_ptr++; // Neuen Pointer im Mapping berechnen. Hier nutze ich eine
649 // Variable mehr als noetig, weil das billiger ist, als jedes
650 // mal ptr=m_ptr*len auszurechnen. Lieber zwei Additionen als
651 // eine Multiplikation und eine Addtion.
652 }
653
654 // Wenn size/len aufgeht, ist das Result-Feld zu gross. Dann bleibt
655 // ein Element leer, das wird hier gestrippt. Das ist billiger als
656 // jedesmal auszurechnen, ob size/len aufgeht.
657 return result-({0});
658
659}
660
661varargs string _send_udp(string mudname, mapping data, int expect_reply) {
662 mixed host_data;
663 string *packet_arr;
664 string packet;
665 int i;
666
667 mudname = lower_case(mudname);
668 if (!(host_data = hosts[mudname])) {
669 string *names;
670
671 if (sizeof(names = expand_mud_name(mudname)) == 1)
672 host_data = hosts[mudname = names[0]];
673 else
674#ifdef SEND_UDP_COMPAT
675 return (string)SEND_UDP_COMPAT(mudname, data, expect_reply);
676#else
677 if (!sizeof(names))
678 return "Unbekannter Mudname: " + capitalize(mudname) + "\n";
679 else
680 return break_string("Mudname ("+capitalize(mudname)+
681 ") nicht eindeutig, es passen: "+implode(map(names,#'capitalize),", ")+
682 ".\n",78);
683#endif
684 }
685
686 if (data[REQUEST] != PING &&
687 data[REQUEST] != QUERY &&
688 data[REQUEST] != REPLY &&
689 member(host_data[HOST_COMMANDS], "*") == -1 &&
690 member(host_data[HOST_COMMANDS], data[REQUEST]) == -1)
691 return capitalize(data[REQUEST]) + ": Command unavailable @" +
692 host_data[HOST_NAME] + "\n";
693
694 data[NAME] = LOCAL_NAME;
695 data[UDP_PORT] = LOCAL_UDP_PORT;
696
697 if (expect_reply) {
698 /* Don't use zero. */
699 data[ID] = ++packet_id;
700 /* Don't need deep_copy() as we are changing the mapping size. */
701 pending_data[mudname + ":" + packet_id] =
702#ifdef INETD_DIAGNOSTICS
703 data + ([ NAME: host_data[HOST_NAME], RESPONSE_TIME: time() ]);
704#else
705 data + ([ NAME: host_data[HOST_NAME] ]);
706#endif
707 }
708 if (!(packet = encode_packet(data))) {
709 if (expect_reply)
710 pending_data = m_copy_delete(pending_data, mudname + ":" + packet_id);
711 log_file(INETD_LOG_FILE, DATE + ": Illegal packet sent by " +
712 object_name(previous_object()) + "\n\n");
713 return "inetd: Illegal packet.\n";
714 }
715 if (expect_reply)
716 call_out("reply_time_out", REPLY_TIME_OUT, mudname + ":" + packet_id);
717
718 if (sizeof(packet) <= MAX_PACKET_LEN)
719 packet_arr = ({ packet });
720 else {
721 string header;
722 int max;
723
724 /* Be careful with the ID. data[ID] could have been set up by RETRY */
725 header =
726 PACKET + ":" + lower_case(LOCAL_NAME) + ":" +
727 ((expect_reply || data[REQUEST] != REPLY)&& data[ID] ?
728 data[ID] : ++packet_id) + ":";
Zesstra2230e282019-09-30 23:05:02 +0200729
MG Mud User88f12472016-06-24 23:31:02 +0200730 /* Allow 8 extra chars: 3 digits + "/" + 3 digits + DELIMITER */
731 packet_arr = explode_packet(packet,
732 MAX_PACKET_LEN - (sizeof(header) + 8));
733
734 for(i = max = sizeof(packet_arr); i--; )
735 packet_arr[i] =
736 header + (i+1) + "/" + max + DELIMITER + packet_arr[i];
737 }
Zesstra2230e282019-09-30 23:05:02 +0200738
MG Mud User88f12472016-06-24 23:31:02 +0200739 for(i = sizeof(packet_arr); i--; )
740 {
741 ZDEBUG(sprintf("%O <- %.500O\n",host_data[HOST_IP], packet_arr[i]));
Zesstra2230e282019-09-30 23:05:02 +0200742 // Transliterationen koennten in : oder DELIMETER
743 // resultieren, weswegen das (hier) nicht geht. Daher bleibt nur
744 // uebrig, alles nicht-ASCII wegzuwerfen.
745 if (!send_udp(host_data[HOST_IP], host_data[HOST_UDP_PORT],
746 to_bytes(packet_arr[i], "ASCII")))
MG Mud User88f12472016-06-24 23:31:02 +0200747 return "inetd: Error in sending packet.\n";
748 }
749 return 0;
750}
751
752void reply_time_out(string id) {
753 mapping data;
754
755 if (data = pending_data[id]) {
756 object ob;
757
758#ifdef INETD_DIAGNOSTICS
759 data[PACKET_LOSS]++;
760#endif
761 if (data[RETRY] < RETRIES) {
762 mapping send;
763
764 data[RETRY]++;
765 /* We must use a copy so the NAME field in pending_data[id]
766 * isn't corrupted by _send_udp(). */
767 send = deep_copy(data);
768 send = m_copy_delete(send, RETRY);
769#ifdef INETD_DIAGNOSTICS
770 send = m_copy_delete(send, PACKET_LOSS);
771 send = m_copy_delete(send, RESPONSE_TIME);
772#endif
773 call_out("reply_time_out", REPLY_TIME_OUT, id);
774 _send_udp(data[NAME], send);
775 return;
776 }
777 data = m_copy_delete(data, RETRY);
778#ifdef INETD_DIAGNOSTICS
779 data = m_copy_delete(data, RESPONSE_TIME);
780#endif
781 catch(call_other(UDP_CMD_DIR + REPLY, "udp_" + REPLY,
782 add_system_field(data, TIME_OUT));publish);
783 /* It's just possible this was removed from the host list. */
784 if (hosts[lower_case(data[NAME])])
785 hosts[lower_case(data[NAME])][HOST_STATUS] = DOWN;
786 remove_incoming(lower_case(data[NAME]) + ":" + id);
787 }
788 pending_data = m_copy_delete(pending_data, id);
789}
790
791void remove_received_id(string id) {
792 received_ids -= ({ id });
793}
794
795varargs mixed query(string what, mixed extra1, mixed extra2) {
796 mixed data;
797
798 switch(what) {
799 case "commands":
800 return COMMANDS;
801 case "hosts":
802 return deep_copy(hosts);
803 case "pending":
804 return deep_copy(pending_data);
805 case "incoming":
806 return deep_copy(incoming_packets);
807 case "received":
808 return ({ }) + received_ids;
809 /* args: "valid_request", request, mudname */
810 case "valid_request":
811 if (data = hosts[extra2])
812 return member(data[HOST_COMMANDS], "*") != -1 ||
813 member(data[HOST_COMMANDS], extra1) != -1;
814 return 0;
815 }
816 return(0);
817}
818