blob: 93b91454b38ff693285a7813443bf8dc11be5481 [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
16
17#include <living/comm.h>
18#define ZDEBUG(x) if (find_player("zesstra"))\
19 find_player("zesstra")->ReceiveMsg(x, \
20 MT_DEBUG|MSG_DONT_STORE,0,"IM: ",this_object())
21
22//#define ZDEBUG(x)
23
24#include <udp.h>
25
26/* --- Configurable definitions. --- */
27
28/* CD muds will probably need these include file. */
29/* #include <std.h> */
30/* #include "/config/sys/local.h" */
31
32/* Public commands which will be accessible to any unlisted muds.
33 * PING, QUERY and REPLY are included by default. */
34#define COMMANDS \
35({ "channel", "finger", "tell", "who", "mail", "www", "htmlwho", "locate" })
36
37//({ "channel", "finger", "ftp", "locate", "man", "tell", "who" })
38
39/* Define this to the object that receives incoming packets and passes
40 * them to the inetd. Undefine for no _receive_udp() security.
41 * NOTE: The string must be of the format that object_name() returns. */
42#define UDP_MASTER __MASTER_OBJECT__
43
44/* How to set the euid for this object if using native mode.
45 * Ensure that it can read the INETD_HOSTS file. */
46#define SET_EUID seteuid(getuid())
47
48/* Define this if you are running another intermud package concurrently. */
49/* #define RECEIVE_UDP_COMPAT(sender, packet) \
50 UDP_MANAGER->incoming_udp(sender, packet) // CD */
51
52/* Define this if you are running another intermud package concurrently and
53 * have a compatability module for it. */
54/* #define SEND_UDP_COMPAT(mudname, data, expect_reply) \
55 "/secure/udp_compat"->_send_udp(mudname, data, expect_reply) */
56
57/* The maximum number of characters we can send in one packet.
58 * You may need to reduce this, but 512 should be safe. */
59#define MAX_PACKET_LEN 1024
60
61/* You shouldn't need to change anything below. */
62
63#define USE_OLD_DELIMITER
64//#define DELIMITER_COMPAT
65#define USE_OLD_DATA_FORMAT
66
67#ifdef ZEBEDEE
68#include <defs.h>
69#endif
70
71#ifndef DATE
72#define DATE ctime(time())[4..15]
73#endif
74
75/* --- End of Config. Do not alter anything below. --- */
76
77#define UNKNOWN 0
78#define UP time()
79#define DOWN (-time())
80
81#define NEW_DELIMITER "\n"
82#ifdef USE_OLD_DELIMITER
83#define DELIMITER "|"
84#else
85#define DELIMITER "\n"
86#endif
87#define OLD_DELIMITER "|"
88#define HOSTFILE_DEL ":"
89#define HOSTFILE_DEL2 ","
90#define RETRY "_RETRY"
91
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 */
266mixed decode(string arg) {
267
268 if (sizeof(arg) && arg[0] == '$')
269 return arg[1..];
270#ifdef RESTRICTED_CASTS
271 if (to_string(to_int(arg)) == arg)
272 return to_int(arg);
273#else
274 if ((string)((int)arg) == arg)
275 return (int)arg;
276#endif
277 return arg;
278}
279
280/*
281 * Decode a UDP packet.
282 * Arguments: UDP packet as a string.
283 * Returns: The decoded information as a mapping, 1 for succes but no
284 * output (buffered packet), or 0 on error.
285 */
286mixed decode_packet(string packet, string delimiter) {
287 string *data;
288 mapping ret;
289 string info, tmp;
290 mixed class;
291 int i, id, n;
292
293 /* If this packet has been split, handle buffering. */
294 if (packet[0..sizeof(PACKET)] == PACKET + ":") {
295 if (sscanf(packet, PACKET + ":%s:%d:%d/%d" + delimiter + "%s",
296 class, id, i, n, tmp) != 5)
297 return 0;
298 class = lower_case(class) + ":" + id;
299 if (pointerp(incoming_packets[class])) {
300 incoming_packets[class][i-1] = tmp;
301 if (member(incoming_packets[class], 0) == -1) {
302 ret =
303 decode_packet(implode(incoming_packets[class], ""), delimiter);
304 remove_incoming(class);
305 return ret;
306 }
307 } else {
308 incoming_packets[class] = allocate(n);
309 incoming_packets[class][i-1] = tmp;
310/* Is it possible to already have a timeout running here ?!? */
311 /* If no timeout is running then start one. */
312 if (!pending_data[class]) {
313 call_out("remove_incoming",
314 REPLY_TIME_OUT + REPLY_TIME_OUT * RETRIES, class);
315 } else {
316 DEBUG("\nINETD: *** Buffered packet Timeout already running! ***\n");
317 }
318 }
319 return 1;
320 }
321 ret = ([ ]);
322 for(i = 0, n = sizeof(data = old_explode(packet, delimiter)); i < n; i++) {
323 /* DATA fields can be denoted by a preceeding blank field. */
324 if (data[i] == "") {
325 tmp = DATA;
326 /* Test for illegal packet length (no DATA) */
327 if (++i >= n)
328 return 0;
329 info = data[i];
330 }
331 else if (sscanf(data[i], "%s:%s", tmp, info) != 2)
332 return 0;
333 switch((string)(class = decode(tmp))) {
334 case DATA:
335#ifdef USE_EXTRACT
336 return ret + ([ DATA: decode(implode(
337 ({ info }) + extract(data, i+1), delimiter)) ]);
338#else
339 return ret + ([ DATA: decode(implode(
340 ({ info }) + data[i+1..], delimiter)) ]);
341#endif
342 case SYSTEM:
343 ret[class] = old_explode(info, ":");
344 continue;
345 default:
346 ret[class] = decode(info);
347 continue;
348 }
349 }
350 return ret;
351}
352
353/* Check wether a UDP packet was valid.
354 * Logs are made and "host" information is updated as appropriate.
355 * Arguments: Decoded UDP packet (mapping)
356 * Returns: 0 for valid packets, an error string otherwise.
357 */
358string valid_request(mapping data) {
359 mixed host_data;
360 string *muds;
361 string req;
362 int i;
363
364 if (!data[NAME] || !data[UDP_PORT])
365 return DATE + ": Illegal packet.\n";
366 if (host_data = hosts[lower_case(data[NAME])]) {
367 if (data[HOST] != host_data[HOST_IP]) {
368 if (data[NAME] == LOCAL_NAME)
369 return DATE + ": *** FAKE MUD ***\n";
370 log_file(INETD_LOG_FILE, DATE + ": Host change:\n" +
371 host_data[HOST_NAME] + ": " + host_data[HOST_IP] + " -> " +
372 data[HOST] + "\n\n");
373 host_data[HOST_IP] = data[HOST];
374 }
375 if (data[UDP_PORT] != host_data[HOST_UDP_PORT]) {
376 if (data[NAME] == LOCAL_NAME)
377 return DATE + ": *** FAKE MUD ***\n";
378 log_file(INETD_LOG_FILE, DATE + ": Port change:\n" +
379 host_data[HOST_NAME] + " (" + host_data[HOST_IP] + "): " +
380 host_data[HOST_UDP_PORT] + " -> " + data[UDP_PORT] + "\n\n");
381 host_data[HOST_UDP_PORT] = data[UDP_PORT];
382 }
383 } else {
384 if (lower_case(data[NAME]) == lower_case(LOCAL_NAME))
385 return DATE + ": *** FAKE MUD ***\n";
386 for(i = sizeof(muds = m_indices(hosts)); i--; ) {
387 host_data = hosts[muds[i]];
388 if (data[HOST] == host_data[HOST_IP] &&
389 data[UDP_PORT] == host_data[HOST_UDP_PORT]) {
390 log_file(INETD_LOG_FILE, DATE + ": Name change:\n" +
391 host_data[HOST_NAME] + " (" + host_data[HOST_IP] +
392 ") -> " + data[NAME] + "\n\n");
393 host_data[HOST_NAME] = data[NAME];
394 hosts[lower_case(data[NAME])] = host_data;
395 m_delete(hosts, muds[i]);
396 i = -2;
397 break;
398 }
399 }
400 if (i != -2) {
401 host_data = hosts[lower_case(data[NAME])] = ({
402 data[NAME],
403 data[HOST],
404 data[UDP_PORT],
405 COMMANDS,
406 ({ "*" }),
407 UP
408 });
409 log_file(INETD_LOG_FILE, DATE + ": New mud.\n" + data[NAME] + ":" +
410 data[HOST] + ":" + data[UDP_PORT] + "\n\n");
411 }
412 }
413 if (!(req = data[REQUEST]))
414 return DATE + ": System message.\n";
415 if (req != PING &&
416 req != QUERY &&
417 req != REPLY &&
418 member(host_data[LOCAL_COMMANDS], req) == -1)
419 {
420 /* This should probably send a system message too. */
421 _send_udp(host_data[HOST_NAME], ([
422 REQUEST: REPLY,
423 RECIPIENT: data[SENDER],
424 ID: data[ID],
425 DATA: "Invalid request @" + LOCAL_NAME + ": " +
426 capitalize(data[REQUEST]) + "\n"
427 ]) );
428 return DATE + ": Invalid request.\n";
429 }
430 return 0;
431}
432
433/*
434 * Incoming UDP packets are sent to this function to be interpretted.
435 * The packet is decoded, checked for validity, HOST_STATUS is updated
436 * and the appropriate udp module called.
437 * Arguments: Senders IP address (string)
438 * UDP packet (string)
439 */
440void _receive_udp(string sender, string packet) {
441 mapping data;
442 string req, err, id;
443
444#ifdef UDP_MASTER
445 if (!previous_object() ||
446 object_name(previous_object()) != UDP_MASTER) {
447 log_file(INETD_LOG_FILE, DATE + ": Illegal call of _receive_udp() by " +
448 object_name(previous_object()) + "\n\n");
449 return;
450 }
451#endif
452
453 ZDEBUG(sprintf("%O -> %.500O\n",sender, packet));
454 if (
455#ifdef DELIMITER_COMPAT
456#ifdef USE_EXTRACT
457 (!(data = decode_packet(packet, NEW_DELIMITER))
458#else
459 (!(data = decode_packet(packet, NEW_DELIMITER))
460#endif
461 || (data[HOST] = sender) && (err = valid_request(data))) &&
462 (!(data = decode_packet(packet, OLD_DELIMITER)) ||
463 (data[HOST] = sender) && (err = valid_request(data)))
464#else
465#ifdef USE_EXTRACT
466 !(data = decode_packet(packet, DELIMITER))
467#else
468 !(data = decode_packet(packet, DELIMITER))
469#endif
470#endif
471 ) {
472 if (!data)
473#ifdef RECEIVE_UDP_COMPAT
474 RECEIVE_UDP_COMPAT(sender, packet);
475#else
476 log_file(INETD_LOG_FILE, DATE + ": Received invalid packet.\nSender: " +
477 sender + "\nPacket:\n" + packet + "\n\n");
478#endif
479 return;
480 }
481#ifdef DELIMITER_COMPAT
482 if (!mappingp(data))
483 return;
484 if (err)
485#else
486 if (!mappingp(data))
487 return;
488 data[HOST] = sender;
489 if (err = valid_request(data))
490#endif
491 {
492 log_file(INETD_LOG_FILE, err + "Sender: " + sender + "\nPacket:\n" +
493 packet + "\n\n");
494 return;
495 }
496 hosts[lower_case(data[NAME])][HOST_STATUS] = UP;
497 if ((req = data[REQUEST]) == REPLY) {
498 mapping pending;
499
500 /* If we can't find the reply in the pending list then bin it. */
501 if (!(pending = pending_data[lower_case(data[NAME]) + ":" + data[ID]]))
502 return;
503 /* Set data[REQUEST] correctly, but still send to (req = REPLY) */
504 data[REQUEST] = pending[REQUEST];
505#ifdef INETD_DIAGNOSTICS
506 data[PACKET_LOSS] = pending[PACKET_LOSS];
507 data[RESPONSE_TIME] = time() - pending[RESPONSE_TIME] + 1;
508#endif
509#if 0
510/* channel replies may not include a recipient, and shouldn't have one set */
511 /* Restore the RECIPIENT in replies if none given and it is known. */
512 if (!data[RECIPIENT] && pending[SENDER])
513 data[RECIPIENT] = pending[SENDER];
514#endif
515 m_delete(pending_data, lower_case(data[NAME]) + ":" + data[ID]);
516 }
517 else if (data[ID]) {
518 id = lower_case(data[NAME]) + ":" + data[ID];
519 if (member(received_ids, id) == -1)
520 {
521 received_ids += ({ id });
522 call_out("remove_received_id",
523 REPLY_TIME_OUT + REPLY_TIME_OUT * (RETRIES + 1), id);
524 }
525 else
526 add_system_field(data, REPEAT);
527 }
528 if (err = catch(
529 call_other(UDP_CMD_DIR + req, "udp_" + req, deep_copy(data));publish))
530 {
531 _send_udp(data[NAME], ([
532 REQUEST: REPLY,
533 RECIPIENT: data[SENDER],
534 ID: data[ID],
535 DATA: capitalize(req)+ " request failed @" + LOCAL_NAME + ".\n"
536 ]) );
537 log_file(INETD_LOG_FILE, DATE + ": " + data[REQUEST] + " from " +
538 data[NAME] + " failed.\n" + err + packet + "\n\n");
539 }
540}
541
542int do_match(string mudname, string match_str) {
543 return mudname[0..sizeof(match_str)-1] == match_str;
544}
545
546#ifdef NO_CLOSURES
547
548status greater(mixed a, mixed b) {
549 return a > b;
550}
551
552string *expand_mud_name(string name) {
553 return sort_array(
554 filter(m_indices(hosts), "do_match", this_object(), name),
555 "greater",
556 this_object()
557 );
558}
559
560#else
561
562string *expand_mud_name(string name) {
563 return sort_array(
564 filter(m_indices(hosts), #'do_match, name),
565 #'>
566 );
567}
568
569#endif
570
571string encode(mixed arg) {
572 if (objectp(arg))
573 return object_name(arg);
574 if (stringp(arg) && sizeof(arg) &&
575 (arg[0] == '$' ||
576#ifdef RESTRICTED_CASTS
577 to_string(to_int(arg)) == (string)arg))
578#else
579 (string)to_int(arg) == (string)arg))
580#endif
581 return "$" + arg;
582 return to_string(arg);
583}
584
585string encode_packet(mapping data) {
586 int i;
587 mixed indices;
588 string header, body, t1, t2;
589 string *ret;
590 status data_flag;
591
592 for(ret = ({ }), i = sizeof(indices = m_indices(data)); i--; ) {
593 if (indices[i] == DATA) {
594 data_flag = 1;
595 continue;
596 }
597 header = encode(indices[i]);
598 body = encode(data[indices[i]]);
599 if (sscanf(header, "%s:%s", t1, t2) ||
600 sscanf(header + body, "%s" + DELIMITER + "%s", t1, t2)
601 )
602 return 0;
603
604 ret += ({ header + ":" + body });
605 }
606 if (data_flag)
607#ifdef USE_OLD_DATA_FORMAT
608 ret += ({ DATA + ":" + encode(data[DATA]) });
609#else
610 ret += ({ "", encode(data[DATA]) });
611#endif
612 return implode(ret, DELIMITER);
613}
614
615// Funktion explode_
616// Die Funktion zerlegt den String packet in gleich lange Teilstrings
617// der Laenge len und gibt die Teilstrings als Array zurueck. Der letzte
618// Teilstring kann kuerzer sein als die anderen.
619string *explode_packet(string packet, int len) {
620
621 int ptr, m_ptr,size;
622 string *result;
623
624 // Variablen initialisieren
625 m_ptr=ptr=0;
626 size=sizeof(packet);
627 result=({});
628
629 // Um Arrayadditionen zu vermeiden wird vorher allokiert. Die Division
630 // durch 0 ist nicht abgefangen, da bei len=0 was im Aufruf falch ist.
631 result=allocate((size/len+1));
632
633 while (ptr<size) {
634 result[m_ptr] = // Aktuellen Teilstring speichern
635#ifdef USE_EXTRACT
636 extract(packet,ptr,ptr+len-1);
637#else
638 packet[ptr..ptr+len-1];
639#endif
640 ptr+=len;// Neuen Pointer im String berechnen
641 m_ptr++; // Neuen Pointer im Mapping berechnen. Hier nutze ich eine
642 // Variable mehr als noetig, weil das billiger ist, als jedes
643 // mal ptr=m_ptr*len auszurechnen. Lieber zwei Additionen als
644 // eine Multiplikation und eine Addtion.
645 }
646
647 // Wenn size/len aufgeht, ist das Result-Feld zu gross. Dann bleibt
648 // ein Element leer, das wird hier gestrippt. Das ist billiger als
649 // jedesmal auszurechnen, ob size/len aufgeht.
650 return result-({0});
651
652}
653
654varargs string _send_udp(string mudname, mapping data, int expect_reply) {
655 mixed host_data;
656 string *packet_arr;
657 string packet;
658 int i;
659
660 mudname = lower_case(mudname);
661 if (!(host_data = hosts[mudname])) {
662 string *names;
663
664 if (sizeof(names = expand_mud_name(mudname)) == 1)
665 host_data = hosts[mudname = names[0]];
666 else
667#ifdef SEND_UDP_COMPAT
668 return (string)SEND_UDP_COMPAT(mudname, data, expect_reply);
669#else
670 if (!sizeof(names))
671 return "Unbekannter Mudname: " + capitalize(mudname) + "\n";
672 else
673 return break_string("Mudname ("+capitalize(mudname)+
674 ") nicht eindeutig, es passen: "+implode(map(names,#'capitalize),", ")+
675 ".\n",78);
676#endif
677 }
678
679 if (data[REQUEST] != PING &&
680 data[REQUEST] != QUERY &&
681 data[REQUEST] != REPLY &&
682 member(host_data[HOST_COMMANDS], "*") == -1 &&
683 member(host_data[HOST_COMMANDS], data[REQUEST]) == -1)
684 return capitalize(data[REQUEST]) + ": Command unavailable @" +
685 host_data[HOST_NAME] + "\n";
686
687 data[NAME] = LOCAL_NAME;
688 data[UDP_PORT] = LOCAL_UDP_PORT;
689
690 if (expect_reply) {
691 /* Don't use zero. */
692 data[ID] = ++packet_id;
693 /* Don't need deep_copy() as we are changing the mapping size. */
694 pending_data[mudname + ":" + packet_id] =
695#ifdef INETD_DIAGNOSTICS
696 data + ([ NAME: host_data[HOST_NAME], RESPONSE_TIME: time() ]);
697#else
698 data + ([ NAME: host_data[HOST_NAME] ]);
699#endif
700 }
701 if (!(packet = encode_packet(data))) {
702 if (expect_reply)
703 pending_data = m_copy_delete(pending_data, mudname + ":" + packet_id);
704 log_file(INETD_LOG_FILE, DATE + ": Illegal packet sent by " +
705 object_name(previous_object()) + "\n\n");
706 return "inetd: Illegal packet.\n";
707 }
708 if (expect_reply)
709 call_out("reply_time_out", REPLY_TIME_OUT, mudname + ":" + packet_id);
710
711 if (sizeof(packet) <= MAX_PACKET_LEN)
712 packet_arr = ({ packet });
713 else {
714 string header;
715 int max;
716
717 /* Be careful with the ID. data[ID] could have been set up by RETRY */
718 header =
719 PACKET + ":" + lower_case(LOCAL_NAME) + ":" +
720 ((expect_reply || data[REQUEST] != REPLY)&& data[ID] ?
721 data[ID] : ++packet_id) + ":";
722
723 /* Allow 8 extra chars: 3 digits + "/" + 3 digits + DELIMITER */
724 packet_arr = explode_packet(packet,
725 MAX_PACKET_LEN - (sizeof(header) + 8));
726
727 for(i = max = sizeof(packet_arr); i--; )
728 packet_arr[i] =
729 header + (i+1) + "/" + max + DELIMITER + packet_arr[i];
730 }
731
732 for(i = sizeof(packet_arr); i--; )
733 {
734 ZDEBUG(sprintf("%O <- %.500O\n",host_data[HOST_IP], packet_arr[i]));
735 if (!send_udp(
736 host_data[HOST_IP], host_data[HOST_UDP_PORT], packet_arr[i]))
737 return "inetd: Error in sending packet.\n";
738 }
739 return 0;
740}
741
742void reply_time_out(string id) {
743 mapping data;
744
745 if (data = pending_data[id]) {
746 object ob;
747
748#ifdef INETD_DIAGNOSTICS
749 data[PACKET_LOSS]++;
750#endif
751 if (data[RETRY] < RETRIES) {
752 mapping send;
753
754 data[RETRY]++;
755 /* We must use a copy so the NAME field in pending_data[id]
756 * isn't corrupted by _send_udp(). */
757 send = deep_copy(data);
758 send = m_copy_delete(send, RETRY);
759#ifdef INETD_DIAGNOSTICS
760 send = m_copy_delete(send, PACKET_LOSS);
761 send = m_copy_delete(send, RESPONSE_TIME);
762#endif
763 call_out("reply_time_out", REPLY_TIME_OUT, id);
764 _send_udp(data[NAME], send);
765 return;
766 }
767 data = m_copy_delete(data, RETRY);
768#ifdef INETD_DIAGNOSTICS
769 data = m_copy_delete(data, RESPONSE_TIME);
770#endif
771 catch(call_other(UDP_CMD_DIR + REPLY, "udp_" + REPLY,
772 add_system_field(data, TIME_OUT));publish);
773 /* It's just possible this was removed from the host list. */
774 if (hosts[lower_case(data[NAME])])
775 hosts[lower_case(data[NAME])][HOST_STATUS] = DOWN;
776 remove_incoming(lower_case(data[NAME]) + ":" + id);
777 }
778 pending_data = m_copy_delete(pending_data, id);
779}
780
781void remove_received_id(string id) {
782 received_ids -= ({ id });
783}
784
785varargs mixed query(string what, mixed extra1, mixed extra2) {
786 mixed data;
787
788 switch(what) {
789 case "commands":
790 return COMMANDS;
791 case "hosts":
792 return deep_copy(hosts);
793 case "pending":
794 return deep_copy(pending_data);
795 case "incoming":
796 return deep_copy(incoming_packets);
797 case "received":
798 return ({ }) + received_ids;
799 /* args: "valid_request", request, mudname */
800 case "valid_request":
801 if (data = hosts[extra2])
802 return member(data[HOST_COMMANDS], "*") != -1 ||
803 member(data[HOST_COMMANDS], extra1) != -1;
804 return 0;
805 }
806 return(0);
807}
808