blob: 23ab51cc17abb7993e18ba59f420265c27589149 [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>
Zesstra06e424d2020-01-13 23:38:28 +010025#include <wizlevels.h>
MG Mud User88f12472016-06-24 23:31:02 +020026
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
93private nosave mapping hosts, pending_data, incoming_packets;
94private nosave mapping static_host_list;
95private nosave string *received_ids;
96private nosave int packet_id;
97
98void set_host_list();
99varargs string _send_udp(string mudname, mapping data, int expect_reply);
100
101#define DEBUG(msg)
102
103void 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
118CREATE_FUN() {
119#elif !defined(COMPAT_FLAG) || defined(ZEBEDEE)
120void create() {
121#else
122void reset(arg) {
123 if (arg)
124 return;
125#endif
126 my_create();
127}
128
129status check_system_field(mapping data, string field) {
130 return data[SYSTEM] && member(data[SYSTEM], field) != -1;
131}
132
133mapping 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
143private 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 */
198void 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
214mixed get_hosts() {return copy(hosts);}
215
216int 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) {
Zesstra12fbad62019-11-28 23:55:44 +0100222 // skip muds we did not hear from for 7 days.
223 if (tmp[HOST_STATUS] + 604800 < time())
224 continue;
MG Mud User88f12472016-06-24 23:31:02 +0200225 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 */
238void 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
Zesstra06e424d2020-01-13 23:38:28 +0100256varargs 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 User88f12472016-06-24 23:31:02 +0200268/*
269 * Remove a buffered packet from the "incoming_packets" mapping.
270 */
271void 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 */
Zesstraac4c5632019-09-30 23:03:04 +0200279string|int decode(string arg)
280{
MG Mud User88f12472016-06-24 23:31:02 +0200281 if (sizeof(arg) && arg[0] == '$')
282 return arg[1..];
Zesstraac4c5632019-09-30 23:03:04 +0200283 else if ((arg & "0123456789") == arg)
MG Mud User88f12472016-06-24 23:31:02 +0200284 return to_int(arg);
Zesstraac4c5632019-09-30 23:03:04 +0200285
MG Mud User88f12472016-06-24 23:31:02 +0200286 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 */
295mixed 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 */
367string 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 */
449void _receive_udp(string sender, string packet) {
Zesstra12fbad62019-11-28 23:55:44 +0100450 mapping|int data;
MG Mud User88f12472016-06-24 23:31:02 +0200451 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
551int do_match(string mudname, string match_str) {
552 return mudname[0..sizeof(match_str)-1] == match_str;
553}
554
555#ifdef NO_CLOSURES
556
557status greater(mixed a, mixed b) {
558 return a > b;
559}
560
561string *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
571string *expand_mud_name(string name) {
572 return sort_array(
573 filter(m_indices(hosts), #'do_match, name),
574 #'>
575 );
576}
577
578#endif
579
580string encode(mixed arg) {
Zesstraac4c5632019-09-30 23:03:04 +0200581 // 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 User88f12472016-06-24 23:31:02 +0200584 if (objectp(arg))
Zesstraac4c5632019-09-30 23:03:04 +0200585 arg = object_name(arg);
Zesstra3af39ae2019-11-28 23:03:24 +0100586 else if (intp(arg))
Zesstraac4c5632019-09-30 23:03:04 +0200587 arg = to_string(arg);
Zesstra3af39ae2019-11-28 23:03:24 +0100588 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))
Zesstrab4782c62019-11-28 23:05:53 +0100596 arg = "$" + arg;
Zesstra3af39ae2019-11-28 23:03:24 +0100597 }
Zesstraac4c5632019-09-30 23:03:04 +0200598 return arg;
MG Mud User88f12472016-06-24 23:31:02 +0200599}
600
601string 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]]);
Zesstra2230e282019-09-30 23:05:02 +0200615 // 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 User88f12472016-06-24 23:31:02 +0200619 if (sscanf(header, "%s:%s", t1, t2) ||
620 sscanf(header + body, "%s" + DELIMITER + "%s", t1, t2)
621 )
622 return 0;
Zesstra2230e282019-09-30 23:05:02 +0200623
MG Mud User88f12472016-06-24 23:31:02 +0200624 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.
639string *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
674varargs 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) + ":";
Zesstra2230e282019-09-30 23:05:02 +0200742
MG Mud User88f12472016-06-24 23:31:02 +0200743 /* 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 }
Zesstra2230e282019-09-30 23:05:02 +0200751
MG Mud User88f12472016-06-24 23:31:02 +0200752 for(i = sizeof(packet_arr); i--; )
753 {
754 ZDEBUG(sprintf("%O <- %.500O\n",host_data[HOST_IP], packet_arr[i]));
Zesstra2230e282019-09-30 23:05:02 +0200755 // 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],
Zesstra97343112019-12-16 22:07:32 +0100759 to_bytes(packet_arr[i], "ASCII//IGNORE")))
MG Mud User88f12472016-06-24 23:31:02 +0200760 return "inetd: Error in sending packet.\n";
761 }
762 return 0;
763}
764
765void 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
804void remove_received_id(string id) {
805 received_ids -= ({ id });
806}
807
808varargs 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