blob: ecf494332295dd5c4080b24439ebd89806c455ec [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;
MG Mud User88f12472016-06-24 23:31:02 +020094private 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
MG Mud User88f12472016-06-24 23:31:02 +0200204 // read the last host file dump and add the static hosts. Then the static
205 // hosts have precedence over the ones from the dump.
206 hosts = (read_host_list(HOST_FILE".dump") || ([])) + static_hosts;
207
208}
209
210mixed get_hosts() {return copy(hosts);}
211
212int dump_hosts_list() {
213
214 write_file(HOST_FILE+".dump", sprintf(
215 "#Hostlist dump from "MUDNAME", created %s\n", strftime()), 1);
216
217 foreach(string mudname, mixed tmp : hosts) {
Zesstra12fbad62019-11-28 23:55:44 +0100218 // skip muds we did not hear from for 7 days.
219 if (tmp[HOST_STATUS] + 604800 < time())
220 continue;
MG Mud User88f12472016-06-24 23:31:02 +0200221 write_file(HOST_FILE+".dump",
222 sprintf("%s:%s:%d:%s:%s\n",tmp[0],tmp[1],tmp[2],implode(tmp[3],","),
223 implode(tmp[4],",")));
224 }
225 return 1;
226}
227
228/*
229 * Make a PING request to all muds in the "hosts" mapping to set
230 * HOST_STATUS information.
231 * But don't ping all muds at once, because that may overflow the callout
232 * table during mud startup, when hundreds of objects make callouts.
233 */
234void startup(string *muds) {
235
236 if (!pointerp(muds))
237 muds=m_indices(hosts);
238 if (!sizeof(muds))
239 return;
240 string *part;
241 if (sizeof(muds) > 9)
242 part=muds[0..9];
243 else
244 part=muds;
245 foreach(string mud: part)
246 _send_udp(mud, ([ REQUEST: PING ]), 1);
247 muds -= part;
248 if (sizeof(muds))
249 call_out(#'startup, 4, muds);
250}
251
Zesstra06e424d2020-01-13 23:38:28 +0100252varargs int remove(int silent)
253{
254 if (!ARCH_SECURITY)
255 return 0;
256
257 // Hostliste dumpen bevor die verlorengeht.
258 dump_hosts_list();
259
260 destruct(this_object());
261 return 1;
262}
263
MG Mud User88f12472016-06-24 23:31:02 +0200264/*
265 * Remove a buffered packet from the "incoming_packets" mapping.
266 */
267void remove_incoming(string id) {
268 m_delete(incoming_packets, id);
269}
270
271/*
272 * Decode a string from a UDP packet.
273 * Returns: The actual value of the argument (either int or string)
274 */
Zesstraac4c5632019-09-30 23:03:04 +0200275string|int decode(string arg)
276{
MG Mud User88f12472016-06-24 23:31:02 +0200277 if (sizeof(arg) && arg[0] == '$')
278 return arg[1..];
Zesstraac4c5632019-09-30 23:03:04 +0200279 else if ((arg & "0123456789") == arg)
MG Mud User88f12472016-06-24 23:31:02 +0200280 return to_int(arg);
Zesstraac4c5632019-09-30 23:03:04 +0200281
MG Mud User88f12472016-06-24 23:31:02 +0200282 return arg;
283}
284
285/*
286 * Decode a UDP packet.
287 * Arguments: UDP packet as a string.
288 * Returns: The decoded information as a mapping, 1 for succes but no
289 * output (buffered packet), or 0 on error.
290 */
291mixed decode_packet(string packet, string delimiter) {
292 string *data;
293 mapping ret;
294 string info, tmp;
295 mixed class;
296 int i, id, n;
297
298 /* If this packet has been split, handle buffering. */
299 if (packet[0..sizeof(PACKET)] == PACKET + ":") {
300 if (sscanf(packet, PACKET + ":%s:%d:%d/%d" + delimiter + "%s",
301 class, id, i, n, tmp) != 5)
302 return 0;
303 class = lower_case(class) + ":" + id;
304 if (pointerp(incoming_packets[class])) {
305 incoming_packets[class][i-1] = tmp;
306 if (member(incoming_packets[class], 0) == -1) {
307 ret =
308 decode_packet(implode(incoming_packets[class], ""), delimiter);
309 remove_incoming(class);
310 return ret;
311 }
312 } else {
313 incoming_packets[class] = allocate(n);
314 incoming_packets[class][i-1] = tmp;
315/* Is it possible to already have a timeout running here ?!? */
316 /* If no timeout is running then start one. */
317 if (!pending_data[class]) {
318 call_out("remove_incoming",
319 REPLY_TIME_OUT + REPLY_TIME_OUT * RETRIES, class);
320 } else {
321 DEBUG("\nINETD: *** Buffered packet Timeout already running! ***\n");
322 }
323 }
324 return 1;
325 }
326 ret = ([ ]);
327 for(i = 0, n = sizeof(data = old_explode(packet, delimiter)); i < n; i++) {
328 /* DATA fields can be denoted by a preceeding blank field. */
329 if (data[i] == "") {
330 tmp = DATA;
331 /* Test for illegal packet length (no DATA) */
332 if (++i >= n)
333 return 0;
334 info = data[i];
335 }
336 else if (sscanf(data[i], "%s:%s", tmp, info) != 2)
337 return 0;
338 switch((string)(class = decode(tmp))) {
339 case DATA:
340#ifdef USE_EXTRACT
341 return ret + ([ DATA: decode(implode(
342 ({ info }) + extract(data, i+1), delimiter)) ]);
343#else
344 return ret + ([ DATA: decode(implode(
345 ({ info }) + data[i+1..], delimiter)) ]);
346#endif
347 case SYSTEM:
348 ret[class] = old_explode(info, ":");
349 continue;
350 default:
351 ret[class] = decode(info);
352 continue;
353 }
354 }
355 return ret;
356}
357
358/* Check wether a UDP packet was valid.
359 * Logs are made and "host" information is updated as appropriate.
360 * Arguments: Decoded UDP packet (mapping)
361 * Returns: 0 for valid packets, an error string otherwise.
362 */
363string valid_request(mapping data) {
364 mixed host_data;
365 string *muds;
366 string req;
367 int i;
368
369 if (!data[NAME] || !data[UDP_PORT])
370 return DATE + ": Illegal packet.\n";
371 if (host_data = hosts[lower_case(data[NAME])]) {
372 if (data[HOST] != host_data[HOST_IP]) {
373 if (data[NAME] == LOCAL_NAME)
374 return DATE + ": *** FAKE MUD ***\n";
375 log_file(INETD_LOG_FILE, DATE + ": Host change:\n" +
376 host_data[HOST_NAME] + ": " + host_data[HOST_IP] + " -> " +
377 data[HOST] + "\n\n");
378 host_data[HOST_IP] = data[HOST];
379 }
380 if (data[UDP_PORT] != host_data[HOST_UDP_PORT]) {
381 if (data[NAME] == LOCAL_NAME)
382 return DATE + ": *** FAKE MUD ***\n";
383 log_file(INETD_LOG_FILE, DATE + ": Port change:\n" +
384 host_data[HOST_NAME] + " (" + host_data[HOST_IP] + "): " +
385 host_data[HOST_UDP_PORT] + " -> " + data[UDP_PORT] + "\n\n");
386 host_data[HOST_UDP_PORT] = data[UDP_PORT];
387 }
388 } else {
389 if (lower_case(data[NAME]) == lower_case(LOCAL_NAME))
390 return DATE + ": *** FAKE MUD ***\n";
391 for(i = sizeof(muds = m_indices(hosts)); i--; ) {
392 host_data = hosts[muds[i]];
393 if (data[HOST] == host_data[HOST_IP] &&
394 data[UDP_PORT] == host_data[HOST_UDP_PORT]) {
395 log_file(INETD_LOG_FILE, DATE + ": Name change:\n" +
396 host_data[HOST_NAME] + " (" + host_data[HOST_IP] +
397 ") -> " + data[NAME] + "\n\n");
398 host_data[HOST_NAME] = data[NAME];
399 hosts[lower_case(data[NAME])] = host_data;
400 m_delete(hosts, muds[i]);
401 i = -2;
402 break;
403 }
404 }
405 if (i != -2) {
406 host_data = hosts[lower_case(data[NAME])] = ({
407 data[NAME],
408 data[HOST],
409 data[UDP_PORT],
410 COMMANDS,
411 ({ "*" }),
412 UP
413 });
414 log_file(INETD_LOG_FILE, DATE + ": New mud.\n" + data[NAME] + ":" +
415 data[HOST] + ":" + data[UDP_PORT] + "\n\n");
416 }
417 }
418 if (!(req = data[REQUEST]))
419 return DATE + ": System message.\n";
420 if (req != PING &&
421 req != QUERY &&
422 req != REPLY &&
423 member(host_data[LOCAL_COMMANDS], req) == -1)
424 {
425 /* This should probably send a system message too. */
426 _send_udp(host_data[HOST_NAME], ([
427 REQUEST: REPLY,
428 RECIPIENT: data[SENDER],
429 ID: data[ID],
430 DATA: "Invalid request @" + LOCAL_NAME + ": " +
431 capitalize(data[REQUEST]) + "\n"
432 ]) );
433 return DATE + ": Invalid request.\n";
434 }
435 return 0;
436}
437
438/*
439 * Incoming UDP packets are sent to this function to be interpretted.
440 * The packet is decoded, checked for validity, HOST_STATUS is updated
441 * and the appropriate udp module called.
442 * Arguments: Senders IP address (string)
443 * UDP packet (string)
444 */
445void _receive_udp(string sender, string packet) {
Zesstra12fbad62019-11-28 23:55:44 +0100446 mapping|int data;
MG Mud User88f12472016-06-24 23:31:02 +0200447 string req, err, id;
448
449#ifdef UDP_MASTER
450 if (!previous_object() ||
451 object_name(previous_object()) != UDP_MASTER) {
452 log_file(INETD_LOG_FILE, DATE + ": Illegal call of _receive_udp() by " +
453 object_name(previous_object()) + "\n\n");
454 return;
455 }
456#endif
457
458 ZDEBUG(sprintf("%O -> %.500O\n",sender, packet));
459 if (
460#ifdef DELIMITER_COMPAT
461#ifdef USE_EXTRACT
462 (!(data = decode_packet(packet, NEW_DELIMITER))
463#else
464 (!(data = decode_packet(packet, NEW_DELIMITER))
465#endif
466 || (data[HOST] = sender) && (err = valid_request(data))) &&
467 (!(data = decode_packet(packet, OLD_DELIMITER)) ||
468 (data[HOST] = sender) && (err = valid_request(data)))
469#else
470#ifdef USE_EXTRACT
471 !(data = decode_packet(packet, DELIMITER))
472#else
473 !(data = decode_packet(packet, DELIMITER))
474#endif
475#endif
476 ) {
477 if (!data)
478#ifdef RECEIVE_UDP_COMPAT
479 RECEIVE_UDP_COMPAT(sender, packet);
480#else
481 log_file(INETD_LOG_FILE, DATE + ": Received invalid packet.\nSender: " +
482 sender + "\nPacket:\n" + packet + "\n\n");
483#endif
484 return;
485 }
486#ifdef DELIMITER_COMPAT
487 if (!mappingp(data))
488 return;
489 if (err)
490#else
491 if (!mappingp(data))
492 return;
493 data[HOST] = sender;
494 if (err = valid_request(data))
495#endif
496 {
497 log_file(INETD_LOG_FILE, err + "Sender: " + sender + "\nPacket:\n" +
498 packet + "\n\n");
499 return;
500 }
501 hosts[lower_case(data[NAME])][HOST_STATUS] = UP;
502 if ((req = data[REQUEST]) == REPLY) {
503 mapping pending;
504
505 /* If we can't find the reply in the pending list then bin it. */
506 if (!(pending = pending_data[lower_case(data[NAME]) + ":" + data[ID]]))
507 return;
508 /* Set data[REQUEST] correctly, but still send to (req = REPLY) */
509 data[REQUEST] = pending[REQUEST];
510#ifdef INETD_DIAGNOSTICS
511 data[PACKET_LOSS] = pending[PACKET_LOSS];
512 data[RESPONSE_TIME] = time() - pending[RESPONSE_TIME] + 1;
513#endif
514#if 0
515/* channel replies may not include a recipient, and shouldn't have one set */
516 /* Restore the RECIPIENT in replies if none given and it is known. */
517 if (!data[RECIPIENT] && pending[SENDER])
518 data[RECIPIENT] = pending[SENDER];
519#endif
520 m_delete(pending_data, lower_case(data[NAME]) + ":" + data[ID]);
521 }
522 else if (data[ID]) {
523 id = lower_case(data[NAME]) + ":" + data[ID];
524 if (member(received_ids, id) == -1)
525 {
526 received_ids += ({ id });
527 call_out("remove_received_id",
528 REPLY_TIME_OUT + REPLY_TIME_OUT * (RETRIES + 1), id);
529 }
530 else
531 add_system_field(data, REPEAT);
532 }
533 if (err = catch(
534 call_other(UDP_CMD_DIR + req, "udp_" + req, deep_copy(data));publish))
535 {
536 _send_udp(data[NAME], ([
537 REQUEST: REPLY,
538 RECIPIENT: data[SENDER],
539 ID: data[ID],
540 DATA: capitalize(req)+ " request failed @" + LOCAL_NAME + ".\n"
541 ]) );
542 log_file(INETD_LOG_FILE, DATE + ": " + data[REQUEST] + " from " +
543 data[NAME] + " failed.\n" + err + packet + "\n\n");
544 }
545}
546
547int do_match(string mudname, string match_str) {
548 return mudname[0..sizeof(match_str)-1] == match_str;
549}
550
551#ifdef NO_CLOSURES
552
553status greater(mixed a, mixed b) {
554 return a > b;
555}
556
557string *expand_mud_name(string name) {
558 return sort_array(
559 filter(m_indices(hosts), "do_match", this_object(), name),
560 "greater",
561 this_object()
562 );
563}
564
565#else
566
567string *expand_mud_name(string name) {
568 return sort_array(
569 filter(m_indices(hosts), #'do_match, name),
570 #'>
571 );
572}
573
574#endif
575
576string encode(mixed arg) {
Zesstraac4c5632019-09-30 23:03:04 +0200577 // Objektnamen mit nicht-ASCII hier ist in allen Faellen doof, wenn sie
578 // koennen auch mit Transliteration nicht mehr verlustfrei zurueck
579 // konvertiert werden...
MG Mud User88f12472016-06-24 23:31:02 +0200580 if (objectp(arg))
Zesstraac4c5632019-09-30 23:03:04 +0200581 arg = object_name(arg);
Zesstra3af39ae2019-11-28 23:03:24 +0100582 else if (intp(arg))
Zesstraac4c5632019-09-30 23:03:04 +0200583 arg = to_string(arg);
Zesstra3af39ae2019-11-28 23:03:24 +0100584 else
585 {
586 if (!stringp(arg))
587 arg = to_string(arg);
588 // faengt es mit $ an oder ist es ne zahl (als string, d.h. enthaelt nur
589 // 0-9)? Dann muss nen $ davor.
590 // TODO: Mhmm. Oder vielleicht doch per RegExp?
591 if ( (arg[0] == '$') || ((arg & "0123456789") == arg))
Zesstrab4782c62019-11-28 23:05:53 +0100592 arg = "$" + arg;
Zesstra3af39ae2019-11-28 23:03:24 +0100593 }
Zesstraac4c5632019-09-30 23:03:04 +0200594 return arg;
MG Mud User88f12472016-06-24 23:31:02 +0200595}
596
597string encode_packet(mapping data) {
598 int i;
599 mixed indices;
Arathornb3051452021-05-13 21:13:03 +0200600 string header, body;
MG Mud User88f12472016-06-24 23:31:02 +0200601 string *ret;
602 status data_flag;
603
604 for(ret = ({ }), i = sizeof(indices = m_indices(data)); i--; ) {
605 if (indices[i] == DATA) {
606 data_flag = 1;
607 continue;
608 }
609 header = encode(indices[i]);
610 body = encode(data[indices[i]]);
Zesstra2230e282019-09-30 23:05:02 +0200611 // Eigentlich sollte encode auch die Wandlung in bytes uebernehmen und
612 // dabei nach ASCII wandeln und ggf. transliterieren. Aber sscanf
613 // braucht wieder strings, daher muss es spaeter passieren (direkt vor
614 // Senden).
Arathornb3051452021-05-13 21:13:03 +0200615 if (sscanf(header, "%~s:%~s") ||
616 sscanf(header + body, "%~s" + DELIMITER + "%~s")
MG Mud User88f12472016-06-24 23:31:02 +0200617 )
618 return 0;
Zesstra2230e282019-09-30 23:05:02 +0200619
MG Mud User88f12472016-06-24 23:31:02 +0200620 ret += ({ header + ":" + body });
621 }
622 if (data_flag)
623#ifdef USE_OLD_DATA_FORMAT
624 ret += ({ DATA + ":" + encode(data[DATA]) });
625#else
626 ret += ({ "", encode(data[DATA]) });
627#endif
628 return implode(ret, DELIMITER);
629}
630
631// Funktion explode_
632// Die Funktion zerlegt den String packet in gleich lange Teilstrings
633// der Laenge len und gibt die Teilstrings als Array zurueck. Der letzte
634// Teilstring kann kuerzer sein als die anderen.
635string *explode_packet(string packet, int len) {
636
637 int ptr, m_ptr,size;
638 string *result;
639
640 // Variablen initialisieren
641 m_ptr=ptr=0;
642 size=sizeof(packet);
643 result=({});
644
645 // Um Arrayadditionen zu vermeiden wird vorher allokiert. Die Division
646 // durch 0 ist nicht abgefangen, da bei len=0 was im Aufruf falch ist.
647 result=allocate((size/len+1));
648
649 while (ptr<size) {
650 result[m_ptr] = // Aktuellen Teilstring speichern
651#ifdef USE_EXTRACT
652 extract(packet,ptr,ptr+len-1);
653#else
654 packet[ptr..ptr+len-1];
655#endif
656 ptr+=len;// Neuen Pointer im String berechnen
657 m_ptr++; // Neuen Pointer im Mapping berechnen. Hier nutze ich eine
658 // Variable mehr als noetig, weil das billiger ist, als jedes
659 // mal ptr=m_ptr*len auszurechnen. Lieber zwei Additionen als
660 // eine Multiplikation und eine Addtion.
661 }
662
663 // Wenn size/len aufgeht, ist das Result-Feld zu gross. Dann bleibt
664 // ein Element leer, das wird hier gestrippt. Das ist billiger als
665 // jedesmal auszurechnen, ob size/len aufgeht.
666 return result-({0});
667
668}
669
670varargs string _send_udp(string mudname, mapping data, int expect_reply) {
671 mixed host_data;
672 string *packet_arr;
673 string packet;
674 int i;
675
676 mudname = lower_case(mudname);
677 if (!(host_data = hosts[mudname])) {
678 string *names;
679
680 if (sizeof(names = expand_mud_name(mudname)) == 1)
681 host_data = hosts[mudname = names[0]];
682 else
683#ifdef SEND_UDP_COMPAT
684 return (string)SEND_UDP_COMPAT(mudname, data, expect_reply);
685#else
686 if (!sizeof(names))
687 return "Unbekannter Mudname: " + capitalize(mudname) + "\n";
688 else
689 return break_string("Mudname ("+capitalize(mudname)+
690 ") nicht eindeutig, es passen: "+implode(map(names,#'capitalize),", ")+
691 ".\n",78);
692#endif
693 }
694
695 if (data[REQUEST] != PING &&
696 data[REQUEST] != QUERY &&
697 data[REQUEST] != REPLY &&
698 member(host_data[HOST_COMMANDS], "*") == -1 &&
699 member(host_data[HOST_COMMANDS], data[REQUEST]) == -1)
700 return capitalize(data[REQUEST]) + ": Command unavailable @" +
701 host_data[HOST_NAME] + "\n";
702
703 data[NAME] = LOCAL_NAME;
704 data[UDP_PORT] = LOCAL_UDP_PORT;
705
706 if (expect_reply) {
707 /* Don't use zero. */
708 data[ID] = ++packet_id;
709 /* Don't need deep_copy() as we are changing the mapping size. */
710 pending_data[mudname + ":" + packet_id] =
711#ifdef INETD_DIAGNOSTICS
712 data + ([ NAME: host_data[HOST_NAME], RESPONSE_TIME: time() ]);
713#else
714 data + ([ NAME: host_data[HOST_NAME] ]);
715#endif
716 }
717 if (!(packet = encode_packet(data))) {
718 if (expect_reply)
719 pending_data = m_copy_delete(pending_data, mudname + ":" + packet_id);
720 log_file(INETD_LOG_FILE, DATE + ": Illegal packet sent by " +
721 object_name(previous_object()) + "\n\n");
722 return "inetd: Illegal packet.\n";
723 }
724 if (expect_reply)
725 call_out("reply_time_out", REPLY_TIME_OUT, mudname + ":" + packet_id);
726
727 if (sizeof(packet) <= MAX_PACKET_LEN)
728 packet_arr = ({ packet });
729 else {
730 string header;
731 int max;
732
733 /* Be careful with the ID. data[ID] could have been set up by RETRY */
734 header =
735 PACKET + ":" + lower_case(LOCAL_NAME) + ":" +
736 ((expect_reply || data[REQUEST] != REPLY)&& data[ID] ?
737 data[ID] : ++packet_id) + ":";
Zesstra2230e282019-09-30 23:05:02 +0200738
MG Mud User88f12472016-06-24 23:31:02 +0200739 /* Allow 8 extra chars: 3 digits + "/" + 3 digits + DELIMITER */
740 packet_arr = explode_packet(packet,
741 MAX_PACKET_LEN - (sizeof(header) + 8));
742
743 for(i = max = sizeof(packet_arr); i--; )
744 packet_arr[i] =
745 header + (i+1) + "/" + max + DELIMITER + packet_arr[i];
746 }
Zesstra2230e282019-09-30 23:05:02 +0200747
MG Mud User88f12472016-06-24 23:31:02 +0200748 for(i = sizeof(packet_arr); i--; )
749 {
750 ZDEBUG(sprintf("%O <- %.500O\n",host_data[HOST_IP], packet_arr[i]));
Zesstra2230e282019-09-30 23:05:02 +0200751 // Transliterationen koennten in : oder DELIMETER
752 // resultieren, weswegen das (hier) nicht geht. Daher bleibt nur
753 // uebrig, alles nicht-ASCII wegzuwerfen.
754 if (!send_udp(host_data[HOST_IP], host_data[HOST_UDP_PORT],
Zesstra97343112019-12-16 22:07:32 +0100755 to_bytes(packet_arr[i], "ASCII//IGNORE")))
MG Mud User88f12472016-06-24 23:31:02 +0200756 return "inetd: Error in sending packet.\n";
757 }
758 return 0;
759}
760
761void reply_time_out(string id) {
762 mapping data;
763
764 if (data = pending_data[id]) {
MG Mud User88f12472016-06-24 23:31:02 +0200765#ifdef INETD_DIAGNOSTICS
766 data[PACKET_LOSS]++;
767#endif
768 if (data[RETRY] < RETRIES) {
769 mapping send;
770
771 data[RETRY]++;
772 /* We must use a copy so the NAME field in pending_data[id]
773 * isn't corrupted by _send_udp(). */
774 send = deep_copy(data);
775 send = m_copy_delete(send, RETRY);
776#ifdef INETD_DIAGNOSTICS
777 send = m_copy_delete(send, PACKET_LOSS);
778 send = m_copy_delete(send, RESPONSE_TIME);
779#endif
780 call_out("reply_time_out", REPLY_TIME_OUT, id);
781 _send_udp(data[NAME], send);
782 return;
783 }
784 data = m_copy_delete(data, RETRY);
785#ifdef INETD_DIAGNOSTICS
786 data = m_copy_delete(data, RESPONSE_TIME);
787#endif
788 catch(call_other(UDP_CMD_DIR + REPLY, "udp_" + REPLY,
789 add_system_field(data, TIME_OUT));publish);
790 /* It's just possible this was removed from the host list. */
791 if (hosts[lower_case(data[NAME])])
792 hosts[lower_case(data[NAME])][HOST_STATUS] = DOWN;
793 remove_incoming(lower_case(data[NAME]) + ":" + id);
794 }
795 pending_data = m_copy_delete(pending_data, id);
796}
797
798void remove_received_id(string id) {
799 received_ids -= ({ id });
800}
801
802varargs mixed query(string what, mixed extra1, mixed extra2) {
803 mixed data;
804
805 switch(what) {
806 case "commands":
807 return COMMANDS;
808 case "hosts":
809 return deep_copy(hosts);
810 case "pending":
811 return deep_copy(pending_data);
812 case "incoming":
813 return deep_copy(incoming_packets);
814 case "received":
815 return ({ }) + received_ids;
816 /* args: "valid_request", request, mudname */
817 case "valid_request":
818 if (data = hosts[extra2])
819 return member(data[HOST_COMMANDS], "*") != -1 ||
820 member(data[HOST_COMMANDS], extra1) != -1;
821 return 0;
822 }
823 return(0);
824}
825