]> asedeno.scripts.mit.edu Git - 1ts-debian.git/blob - zephyr/server/dispatch.c
c4c1026b89d0d1164c6269948e57fa542932dc04
[1ts-debian.git] / zephyr / server / dispatch.c
1 /* This file is part of the Project Athena Zephyr Notification System.
2  * It contains functions for dispatching a notice.
3  *
4  *      Created by:     John T. Kohl
5  *
6  *      $Source: /afs/dev.mit.edu/source/repository/athena/lib/zephyr/server/dispatch.c,v $
7  *      $Author$
8  *
9  *      Copyright (c) 1987, 1991 by the Massachusetts Institute of Technology.
10  *      For copying and distribution information, see the file
11  *      "mit-copyright.h". 
12  */
13
14 #include <zephyr/mit-copyright.h>
15 #include "zserver.h"
16 #include <sys/socket.h>
17 #include <com_err.h>
18
19 #ifndef lint
20 #ifndef SABER
21 static const char rcsid_dispatch_c[] =
22 "$Id$";
23 #endif
24 #endif
25
26 #define NACKTAB_HASHSIZE                1023
27 #define NACKTAB_HASHVAL(sockaddr, uid)  (((sockaddr).sin_addr.s_addr ^ \
28                                           (sockaddr).sin_port ^ \
29                                           (uid).zuid_addr.s_addr ^ \
30                                           (uid).tv.tv_sec ^ \
31                                           (uid).tv.tv_usec) % NACKTAB_HASHSIZE)
32 #define HOSTS_SIZE_INIT                 256
33
34 #ifdef DEBUG
35 ZCONST char *ZNoticeKinds[9] = {"UNSAFE", "UNACKED", "ACKED", "HMACK",
36                                     "HMCTL", "SERVACK", "SERVNAK", "CLIENTACK",
37                                     "STAT"};
38 #endif
39 /*
40  *
41  * External Routines:
42  *
43  * void dispatch(notice, auth, who)
44  *      ZNotice_t *notice;
45  *      int auth;
46  *      struct sockaddr_in *who;
47  *
48  * void clt_ack(notice, who, sent)
49  *      ZNotice_t *notice;
50  *      struct sockaddr_in *who;
51  *      Sent_type sent;
52  *
53  * void nack_release(client)
54  *      Client *client;
55  *
56  * void sendit(notice, auth, who, external)
57  *      ZNotice_t *notice;
58  *      int auth;
59  *      struct sockaddr_in *who;
60  *      int external;
61  *
62  * void xmit(notice, dest, auth, client)
63  *      ZNotice_t *notice;
64  *      struct sockaddr_in *dest;
65  *      int auth;
66  *      Client *client;
67  */
68
69
70 String *class_control, *class_admin, *class_hm, *class_ulogin, *class_ulocate;
71
72 int rexmit_times[] = REXMIT_TIMES;
73
74 static void nack_cancel __P((ZNotice_t *, struct sockaddr_in *));
75 static void dispatch __P((ZNotice_t *, int, struct sockaddr_in *, int));
76 static int send_to_dest __P((ZNotice_t *, int, Destination *dest, int, int));
77 static void hostm_deathgram __P((struct sockaddr_in *, Server *));
78 static char *hm_recipient __P((void));
79
80 Statistic realm_notices = {0, "inter-realm notices"};
81 Statistic interserver_notices = {0, "inter-server notices"};
82 Statistic hm_packets = {0, "hostmanager packets"};
83 Statistic control_notices = {0, "client control notices"};
84 Statistic message_notices = {0, "message notices"};
85 Statistic login_notices = {0, "login notices"};
86 Statistic i_s_ctls = {0, "inter-server control notices"};
87 Statistic i_s_logins = {0, "inter-server login notices"};
88 Statistic i_s_admins = {0, "inter-server admin notices"};
89 Statistic i_s_locates = {0, "inter-server locate notices"};
90 Statistic locate_notices = {0, "locate notices"};
91 Statistic admin_notices = {0, "admin notices"};
92
93 static Unacked *nacktab[NACKTAB_HASHSIZE];
94 static struct in_addr *hosts;
95 static int hosts_size = 0, num_hosts = 0;
96
97 static void
98 dump_stats (arg)
99     void *arg;
100 {
101     syslog(LOG_INFO, "stats: %s: %d", hm_packets.str, hm_packets.val);
102     syslog(LOG_INFO, "stats: %s: %d", control_notices.str,
103            control_notices.val);
104     syslog(LOG_INFO, "stats: %s: %d", message_notices.str,
105            message_notices.val);
106     syslog(LOG_INFO, "stats: %s: %d", login_notices.str, login_notices.val);
107     syslog(LOG_INFO, "stats: %s: %d", locate_notices.str, locate_notices.val);
108     syslog(LOG_INFO, "stats: %s: %d", admin_notices.str, admin_notices.val);
109     syslog(LOG_INFO, "stats: %s: %d", realm_notices.str, realm_notices.val);
110     syslog(LOG_INFO, "stats: %s: %d", interserver_notices.str,
111            interserver_notices.val);
112     syslog(LOG_INFO, "stats: %s: %d", i_s_ctls.str, i_s_ctls.val);
113     syslog(LOG_INFO, "stats: %s: %d", i_s_logins.str, i_s_logins.val);
114     syslog(LOG_INFO, "stats: %s: %d", i_s_admins.str, i_s_admins.val);
115     syslog(LOG_INFO, "stats: %s: %d", i_s_locates.str, i_s_locates.val);
116
117     /* log stuff once an hour */
118     timer_set_rel ((long) 6*60*60, dump_stats, arg);
119 }
120
121 /*
122  * Handle an input packet.
123  * Warning: this function may be called from within a brain dump.
124  */
125
126 void
127 handle_packet()
128 {
129     Code_t status;
130     ZPacket_t input_packet;     /* from the network */
131     ZNotice_t new_notice;       /* parsed from input_packet */
132     int input_len;              /* len of packet */
133     struct sockaddr_in input_sin; /* Zconstructed for authent */
134     struct sockaddr_in whoisit; /* for holding peer's address */
135     int authentic;              /* authentic flag */
136     Pending *pending;           /* pending packet */
137     int from_server;            /* packet is from another server */
138     ZRealm *realm;              /* foreign realm ptr */
139 #ifdef DEBUG
140     static int first_time = 1;
141 #endif
142
143 #ifdef DEBUG
144     /* Dump statistics five minutes after startup */
145     if (first_time) {
146         first_time = 0;
147         timer_set_rel(5*60, dump_stats, NULL);
148     }
149 #endif
150     /* handle traffic */
151
152     if (otherservers[me_server_idx].queue) {
153         /* something here for me; take care of it */
154 #if 1
155         zdbug((LOG_DEBUG, "internal queue process"));
156 #endif
157
158         pending = server_dequeue(me_server);
159
160         status = ZParseNotice(pending->packet, pending->len, &new_notice);
161         if (status != ZERR_NONE) {
162             syslog(LOG_ERR, "bad notice parse (%s): %s",
163                    inet_ntoa(pending->who.sin_addr), error_message(status));
164         } else {
165             dispatch(&new_notice, pending->auth, &pending->who, 1);
166         }
167         server_pending_free(pending);
168         return;
169     }
170
171     /* 
172      * nothing in internal queue, go to the external library
173      * queue/socket
174      */
175     status = ZReceivePacket(input_packet, &input_len, &whoisit);
176     if (status != ZERR_NONE) {
177         syslog(LOG_ERR, "bad packet receive: %s from %s",
178                error_message(status), inet_ntoa(whoisit.sin_addr));
179         return;
180     }
181     npackets++;
182     status = ZParseNotice(input_packet, input_len, &new_notice);
183     if (status != ZERR_NONE) {
184         syslog(LOG_ERR, "bad notice parse (%s): %s",
185                inet_ntoa(whoisit.sin_addr), error_message(status));
186         return;
187     }
188     if (server_which_server(&whoisit)) {
189         /* we need to parse twice--once to get
190            the source addr, second to check
191            authentication */
192         memset(&input_sin, 0, sizeof(input_sin));
193         input_sin.sin_addr.s_addr = new_notice.z_sender_addr.s_addr;
194         input_sin.sin_port = new_notice.z_port;
195         input_sin.sin_family = AF_INET;
196         /* Should check to see if packet is from another realm's server, 
197            or a client */
198         /* Clients don't check auth of acks, nor do we make it so they
199            can in general, so this is safe. */
200         if (new_notice.z_kind == SERVACK || new_notice.z_kind == SERVNAK) {
201           authentic = ZAUTH_YES;
202         } else {
203           if (realm = realm_which_realm(&input_sin)) {
204             authentic = ZCheckRealmAuthentication(&new_notice,
205                                                        &input_sin,
206                                                        realm->name);
207           } else 
208             authentic = ZCheckAuthentication(&new_notice, &input_sin);
209         }
210         from_server = 1;
211     } else {
212         from_server = 0;
213         /* Clients don't check auth of acks, nor do we make it so they
214            can in general, so this is safe. */
215         if (new_notice.z_kind == SERVACK || new_notice.z_kind == SERVNAK) {
216           authentic = ZAUTH_YES;
217         } else {
218           if (realm = realm_which_realm(&whoisit)) {
219             authentic = ZCheckRealmAuthentication(&new_notice,
220                                                   &whoisit,
221                                                   realm->name);
222           } else
223             authentic = ZCheckAuthentication(&new_notice, &whoisit);
224         }
225     }
226
227 #if 0
228     if (whoisit.sin_port != hm_port && whoisit.sin_port != hm_srv_port &&
229         strcasecmp(new_notice.z_class, ZEPHYR_ADMIN_CLASS) != 0 &&
230         whoisit.sin_port != srv_addr.sin_port &&
231         new_notice.z_kind != CLIENTACK) {
232         syslog(LOG_ERR, "bad port %s/%d", inet_ntoa(whoisit.sin_addr),
233                ntohs(whoisit.sin_port));
234         return;
235     }
236 #endif
237
238     message_notices.val++;
239     dispatch(&new_notice, authentic, &whoisit, from_server);
240     return;
241 }
242 /*
243  * Dispatch a notice.
244  */
245
246 static void
247 dispatch(notice, auth, who, from_server)
248     ZNotice_t *notice;
249     int auth;
250     struct sockaddr_in *who;
251     int from_server;
252 {
253     Code_t status;
254     String *notice_class;
255     struct sockaddr_in who2;
256     int authflag;
257     ZRealm *realm;
258     char *cp;
259 #ifdef DEBUG
260     char dbg_buf[BUFSIZ];
261 #endif
262
263     authflag = (auth == ZAUTH_YES);
264
265     if ((int) notice->z_kind < (int) UNSAFE ||
266         (int) notice->z_kind > (int) CLIENTACK) {
267         syslog(LOG_NOTICE, "bad notice kind 0x%x from %s", notice->z_kind,
268                inet_ntoa(who->sin_addr));
269         return;
270     }
271 #if 0
272     if (zdebug) {
273         syslog(LOG_DEBUG,
274                 "disp:%s '%s' '%s' '%s' notice to '%s' from '%s' %s/%d/%d",
275                 ZNoticeKinds[(int) notice->z_kind], notice->z_class,
276                 notice->z_class_inst, notice->z_opcode, notice->z_recipient,
277                 notice->z_sender, inet_ntoa(who->sin_addr),
278                 ntohs(who->sin_port), ntohs(notice->z_port));
279     }
280 #endif
281
282     if (notice->z_kind == CLIENTACK) {
283         nack_cancel(notice, who);
284         return;
285     }
286
287     who2 = *who;
288 #if 0
289     if (0 && from_server) {
290         /* incorporate server_dispatch here */
291     }
292 #endif
293     notice_class = make_string(notice->z_class,1);
294
295     if (from_server) {
296         interserver_notices.val++;
297         status = server_dispatch(notice, authflag, who);
298     } else if (class_is_hm(notice_class)) {
299         hm_packets.val++;
300         status = hostm_dispatch(notice, authflag, who, me_server);
301     } else if (realm_which_realm(who) && !(class_is_admin(notice_class))) {
302         realm_notices.val++;
303         status = realm_dispatch(notice, authflag, who, me_server);
304     } else if (class_is_control(notice_class)) {
305         control_notices.val++;
306         status = control_dispatch(notice, authflag, who, me_server);
307     } else if (class_is_ulogin(notice_class)) {
308         login_notices.val++;
309         status = ulogin_dispatch(notice, authflag, who, me_server);
310     } else if (class_is_ulocate(notice_class)) {
311         locate_notices.val++;
312         status = ulocate_dispatch(notice, authflag, who, me_server);
313     } else if (class_is_admin(notice_class)) {
314         admin_notices.val++;
315         status = server_adispatch(notice, authflag, who, me_server);
316     } else {
317         if (!realm_bound_for_realm(ZGetRealm(), notice->z_recipient)) {
318             cp = strchr(notice->z_recipient, '@');
319             if (!cp ||
320                 !(realm = realm_get_realm_by_name(cp + 1))) {
321                 /* Foreign user, local realm */
322                 sendit(notice, authflag, who, 0);
323             } else
324                 realm_handoff(notice, authflag, who, realm, 1);
325         } else {
326             if (notice->z_recipient[0] == '@')
327                 notice->z_recipient = "";
328             sendit(notice, authflag, who, 1);
329         }
330         free_string(notice_class);
331         return;
332     }
333
334     if (status == ZSRV_REQUEUE)
335         server_self_queue(notice, authflag, who);
336     free_string(notice_class);
337 }
338
339 /*
340  * Send a notice off to those clients who have subscribed to it.
341  */
342
343 void
344 sendit(notice, auth, who, external)
345     ZNotice_t *notice;
346     int auth;
347     struct sockaddr_in *who;
348     int external;
349 {
350     static int send_counter = 0;
351     char recipbuf[ANAME_SZ + INST_SZ + REALM_SZ + 3], *recipp;
352     int any = 0;
353     Acl *acl;
354     Destination dest;
355     String *class;
356
357     class = make_string(notice->z_class, 1);
358     if (realm_bound_for_realm(ZGetRealm(), notice->z_recipient)) {
359       ZRealm *rlm;
360
361       acl = class_get_acl(class);
362       if (acl != NULL) {
363         /* if controlled and not auth, fail */
364         if (!auth) {
365             syslog(LOG_WARNING, "sendit unauthentic %s from %s",
366                    notice->z_class, notice->z_sender);
367             clt_ack(notice, who, AUTH_FAILED);
368             free_string(class);
369             return;
370         }
371         /* if from foreign realm server, disallow if not realm of sender */
372         rlm = realm_which_realm(who);
373         if (rlm) {
374           if (!realm_sender_in_realm(rlm->name, notice->z_sender)) {
375             syslog(LOG_WARNING, "sendit auth not verifiable %s (%s) from %s",
376                    notice->z_class, rlm->name, notice->z_sender);
377             clt_ack(notice, who, AUTH_FAILED);
378             free_string(class);
379             return;
380           }
381         }
382         /* if not auth to transmit, fail */
383         if (!access_check(notice->z_sender, acl, TRANSMIT)) {
384             syslog(LOG_WARNING, "sendit unauthorized %s from %s",
385                    notice->z_class, notice->z_sender);
386             clt_ack(notice, who, AUTH_FAILED);
387             free_string(class);
388             return;
389         }
390         /* sender != inst and not auth to send to others --> fail */
391         if (strcmp(notice->z_sender, notice->z_class_inst) != 0 &&
392             !access_check(notice->z_sender, acl, INSTUID)) {
393             syslog(LOG_WARNING, "sendit unauth uid %s %s.%s", notice->z_sender,
394                    notice->z_class, notice->z_class_inst);
395             clt_ack(notice, who, AUTH_FAILED);
396             free_string(class);
397             return;
398         }
399       }
400     }
401     if (!realm_which_realm(who)) {
402         if (memcmp(&notice->z_sender_addr.s_addr, &who->sin_addr.s_addr,
403                    sizeof(notice->z_sender_addr.s_addr))) {
404             /* someone is playing games... */
405             /* inet_ntoa returns pointer to static area */
406             /* max size is 255.255.255.255 */
407             char buffer[16];
408             strcpy(buffer, inet_ntoa(who->sin_addr));
409             if (!auth) {
410                 syslog(LOG_WARNING,
411                        "sendit unauthentic fake packet: claimed %s, real %s",
412                        inet_ntoa(notice->z_sender_addr), buffer);
413                 clt_ack(notice, who, AUTH_FAILED);
414                 free_string(class);
415                 return;
416             }
417             if (ntohl(notice->z_sender_addr.s_addr) != 0) {
418                 syslog(LOG_WARNING,
419                        "sendit invalid address: claimed %s, real %s",
420                        inet_ntoa(notice->z_sender_addr), buffer);
421                 clt_ack(notice, who, AUTH_FAILED);
422                 free_string(class);
423                 return;
424             }
425             syslog(LOG_WARNING, "sendit addr mismatch: claimed %s, real %s",
426                    inet_ntoa(notice->z_sender_addr), buffer);
427         }
428     }
429
430     /* Increment the send counter, used to prevent duplicate sends to
431      * clients.  On the off-chance that we wrap around to 0, skip over
432      * it to prevent missing clients which have never had a packet
433      * sent to them. */
434     send_counter++;
435     if (send_counter == 0)
436         send_counter = 1;
437
438     /* Send to clients subscribed to the triplet itself. */
439     dest.classname = class;
440     dest.inst = make_string(notice->z_class_inst, 1);
441     if (realm_bound_for_realm(ZGetRealm(), notice->z_recipient) && 
442         *notice->z_recipient == '@') 
443       dest.recip = make_string("", 0);
444     else {
445       strncpy(recipbuf, notice->z_recipient, sizeof(recipbuf));
446       recipp = strrchr(recipbuf, '@');
447       if (recipp)
448         sprintf(recipp + 1, "%s", realm_expand_realm(recipp + 1));
449       dest.recip = make_string(recipbuf, 0);
450     }
451
452     if (send_to_dest(notice, auth, &dest, send_counter, external))
453         any = 1;
454
455     /* Send to clients subscribed to the triplet with the instance
456      * substituted with the wildcard instance. */
457     free_string(dest.inst);
458     dest.inst = wildcard_instance;
459     if (send_to_dest(notice, auth, &dest, send_counter, external))
460         any = 1;
461
462     free_string(class);
463     free_string(dest.recip);
464     if (any)
465         ack(notice, who);
466     else
467         nack(notice, who);
468 }
469
470 /*
471  * Send to each client in the list.  Avoid duplicates by setting
472  * last_send on each client to send_counter, a nonce which is updated
473  * by sendit() above.
474  */
475
476 static int
477 send_to_dest(notice, auth, dest, send_counter, external)
478     ZNotice_t *notice;
479     int auth;
480     Destination *dest;
481     int send_counter;
482     int external;
483 {
484     Client **clientp;
485     int any = 0;
486
487     clientp = triplet_lookup(dest);
488     if (!clientp)
489         return 0;
490
491     for (; *clientp; clientp++) {
492         if ((*clientp)->last_send == send_counter)
493             continue;
494         (*clientp)->last_send = send_counter;
495         if ((*clientp)->realm) {
496           if (external) {
497             realm_handoff(notice, auth, &clientp[0]->addr, clientp[0]->realm,
498                           1);
499             any = 1;
500           }
501         } else {
502             xmit(notice, &((*clientp)->addr), auth, *clientp);
503             any = 1;
504         }
505     }
506
507     return any;
508 }
509
510 /*
511  * Release anything destined for the client in the not-yet-acked table.
512  */
513
514 void
515 nack_release(client)
516     Client *client;
517 {
518     int i;
519     Unacked *nacked, *next;
520
521     for (i = 0; i < NACKTAB_HASHSIZE; i++) {
522         for (nacked = nacktab[i]; nacked; nacked = next) {
523             next = nacked->next;
524             if (nacked->client == client) {
525                 timer_reset(nacked->timer);
526                 LIST_DELETE(nacked);
527                 free(nacked->packet);
528                 free(nacked);
529             }
530         }
531     }
532 }
533
534 /*
535  * Send one packet of a fragmented message to a client.  After transmitting,
536  * put it onto the not ack'ed list.
537  */
538
539 /* the arguments must be the same as the arguments to Z_XmitFragment */
540 /*ARGSUSED*/
541 Code_t
542 xmit_frag(notice, buf, len, waitforack)
543     ZNotice_t *notice;
544     char *buf;
545     int len;
546     int waitforack;
547 {
548     struct sockaddr_in sin;
549     char *savebuf;
550     Unacked *nacked;
551     Code_t retval;
552     int hashval, sendfail = 0;
553
554     retval = ZSendPacket(buf, len, 0);
555     if (retval != ZERR_NONE) {
556         syslog(LOG_WARNING, "xmit_frag send: %s", error_message(retval));
557         if (retval != EAGAIN && retval != ENOBUFS)
558             return retval;
559         sendfail = 1;
560     }
561
562     /* now we've sent it, mark it as not ack'ed */
563     nacked = (Unacked *) malloc(sizeof(Unacked));
564     if (!nacked) {
565         /* no space: just punt */
566         syslog(LOG_WARNING, "xmit_frag nack malloc");
567         return ENOMEM;
568     }
569
570     savebuf = (char *) malloc(len);
571     if (!savebuf) {
572         /* no space: just punt */
573         syslog(LOG_WARNING, "xmit_frag pack malloc");
574         free(nacked);
575         return ENOMEM;
576     }
577
578     memcpy(savebuf, buf, len);
579
580     sin = ZGetDestAddr();
581     nacked->client = NULL;
582     nacked->rexmits = (sendfail) ? -1 : 0;
583     nacked->packet = savebuf;
584     nacked->dest.addr = sin;
585     nacked->packsz = len;
586     nacked->uid = notice->z_uid;
587     nacked->timer = timer_set_rel(rexmit_times[0], rexmit, nacked);
588     LIST_INSERT(&nacktab[NACKTAB_HASHVAL(sin, nacked->uid)], nacked);
589     return(ZERR_NONE);
590 }
591
592
593 /*
594  * Send the notice to the client.  After transmitting, put it onto the
595  * not ack'ed list.
596  */
597
598 void
599 xmit(notice, dest, auth, client)
600     ZNotice_t *notice;
601     struct sockaddr_in *dest;
602     int auth;
603     Client *client;
604 {
605     char *noticepack;
606     Unacked *nacked;
607     int packlen, sendfail = 0;
608     Code_t retval;
609
610 #if 0
611     zdbug((LOG_DEBUG,"xmit"));
612 #endif
613
614     noticepack = (char *) malloc(sizeof(ZPacket_t));
615     if (!noticepack) {
616         syslog(LOG_ERR, "xmit malloc");
617         return;                 /* DON'T put on nack list */
618     }
619         
620     packlen = sizeof(ZPacket_t);
621
622     if (auth && client) {       /*
623                                    we are distributing authentic and
624                                    we have a pointer to auth info
625                                    */
626 #ifdef HAVE_KRB5
627          retval = ZFormatAuthenticNoticeV5(notice, noticepack, packlen,
628                                            &packlen, client->session_keyblock);
629          if (retval != ZERR_NONE) {
630               syslog(LOG_ERR, "xmit auth format: %s", error_message(retval));
631               free(noticepack);
632               return;
633          }
634 #else
635 #if defined(HAVE_KRB4)
636          retval = ZFormatAuthenticNotice(notice, noticepack, packlen,
637                                            &packlen, client->session_key);
638          if (retval != ZERR_NONE) {
639               syslog(LOG_ERR, "xmit auth format: %s", error_message(retval));
640               free(noticepack);
641               return;
642          }
643 #else /* !HAVE_KRB4 */
644         notice->z_auth = 1;
645         retval = ZFormatSmallRawNotice(notice, noticepack, &packlen);
646         if (retval != ZERR_NONE) {
647             syslog(LOG_ERR, "xmit auth/raw format: %s", error_message(retval));
648             free(noticepack);
649             return;
650         }
651 #endif /* HAVE_KRB4 */
652 #endif /* HAVE_KRB5 */
653     } else {
654         notice->z_auth = 0;
655         notice->z_authent_len = 0;
656         notice->z_ascii_authent = (char *)"";
657         retval = ZFormatSmallRawNotice(notice, noticepack, &packlen);
658         /* This code is needed because a Zephyr can "grow" when a remote 
659          * realm name is inserted into the Zephyr before being resent out
660          * locally. It essentially matches the code in realm.c to do the
661          * same thing with authentic Zephyrs.
662          */
663         if (retval == ZERR_PKTLEN) {
664           ZNotice_t partnotice, newnotice;
665           char multi[64];
666           char *buffer, *ptr;
667           int buffer_len, hdrlen, offset, fragsize, ret_len, message_len;
668           int origoffset, origlen;
669
670           free(noticepack);
671
672           retval = ZSetDestAddr(dest);
673           if (retval != ZERR_NONE) {
674             syslog(LOG_WARNING, "xmit set addr: %s", error_message(retval));
675             return;
676           }
677
678           partnotice = *notice;
679           
680           partnotice.z_auth = 0;
681           partnotice.z_authent_len = 0;
682           partnotice.z_ascii_authent = (char *)"";
683
684           origoffset = offset = fragsize = 0;
685           origlen = notice->z_message_len;
686
687           buffer = (char *) malloc(sizeof(ZPacket_t));
688           if (!buffer) {
689             syslog(LOG_ERR, "xmit unauth refrag malloc");
690             return;                 /* DON'T put on nack list */
691           }
692           buffer_len = sizeof(ZPacket_t);
693
694           retval = Z_FormatRawHeader(&partnotice, buffer, buffer_len, 
695                                      &hdrlen, NULL, NULL);
696           if (retval != ZERR_NONE) {
697             syslog(LOG_ERR, "xmit unauth refrag fmt: failed");
698             free(buffer);
699             return;
700           }
701
702           if (notice->z_multinotice && strcmp(notice->z_multinotice, ""))
703             if (sscanf(notice->z_multinotice, "%d/%d", &origoffset, &origlen)
704                 != 2) 
705               {
706                 syslog(LOG_WARNING, "xmit unauth refrag: parse failed");
707                 free(buffer);
708                 return;
709               }
710
711               fragsize = Z_MAXPKTLEN-hdrlen-Z_FRAGFUDGE;
712
713           while (offset < notice->z_message_len || !notice->z_message_len) {
714             (void) sprintf(multi, "%d/%d", offset+origoffset, origlen);
715             partnotice.z_multinotice = multi;
716             if (offset > 0) {
717               (void) gettimeofday(&partnotice.z_uid.tv, (struct timezone *)0);
718               partnotice.z_uid.tv.tv_sec = htonl((u_long)
719                                                  partnotice.z_uid.tv.tv_sec);
720               partnotice.z_uid.tv.tv_usec = htonl((u_long)
721                                                   partnotice.z_uid.tv.tv_usec);
722               (void) memcpy((char *)&partnotice.z_uid.zuid_addr, &__My_addr,
723                             sizeof(__My_addr));
724             }
725             partnotice.z_message = notice->z_message+offset;
726             message_len = min(notice->z_message_len-offset, fragsize);
727             partnotice.z_message_len = message_len;
728
729             retval = Z_FormatRawHeader(&partnotice, buffer, buffer_len, 
730                                        &hdrlen, &ptr, NULL);
731             if (retval != ZERR_NONE) {
732               syslog(LOG_WARNING, "xmit unauth refrag raw: %s",
733                      error_message(retval));
734               free(buffer);
735               return;
736             }
737
738             ptr = buffer+hdrlen;
739
740             (void) memcpy(ptr, partnotice.z_message, partnotice.z_message_len);
741             
742             buffer_len = hdrlen+partnotice.z_message_len;
743
744             xmit_frag(&partnotice, buffer, buffer_len, 0);
745
746             offset += fragsize;
747
748             if (!notice->z_message_len)
749               break;
750           }
751           free(buffer);
752           return;
753         }
754         /* End of refrag code */
755
756         if (retval != ZERR_NONE) {
757             syslog(LOG_ERR, "xmit format: %s", error_message(retval));
758             free(noticepack);
759             return;                     /* DON'T put on nack list */
760         }
761     }
762 #if 0
763     zdbug((LOG_DEBUG," to %s/%d", inet_ntoa(dest->sin_addr),
764            ntohs(dest->sin_port)));
765 #endif
766     retval = ZSetDestAddr(dest);
767     if (retval != ZERR_NONE) {
768         syslog(LOG_WARNING, "xmit set addr: %s", error_message(retval));
769         free(noticepack);
770         return;
771     }
772     retval = ZSendPacket(noticepack, packlen, 0);
773     if (retval != ZERR_NONE) {
774         syslog(LOG_WARNING, "xmit xmit: (%s/%d) %s", inet_ntoa(dest->sin_addr),
775                ntohs(dest->sin_port), error_message(retval));
776         if (retval != EAGAIN && retval != ENOBUFS) {
777             free(noticepack);
778             return;
779         }
780         sendfail = 1;
781     }
782
783     /* now we've sent it, mark it as not ack'ed */
784
785     nacked = (Unacked *) malloc(sizeof(Unacked));
786     if (!nacked) {
787         /* no space: just punt */
788         syslog(LOG_WARNING, "xmit nack malloc");
789         free(noticepack);
790         return;
791     }
792
793     nacked->client = client;
794     nacked->rexmits = (sendfail) ? -1 : 0;
795     nacked->packet = noticepack;
796     nacked->dest.addr = *dest;
797     nacked->packsz = packlen;
798     nacked->uid = notice->z_uid;
799     nacked->timer = timer_set_rel(rexmit_times[0], rexmit, nacked);
800     LIST_INSERT(&nacktab[NACKTAB_HASHVAL(*dest, nacked->uid)], nacked);
801 }
802
803 /*
804  * Retransmit the packet specified.  If we have timed out or retransmitted
805  * too many times, punt the packet and initiate the host recovery algorithm
806  * Else, increment the count and re-send the notice packet.
807  */
808
809 void
810 rexmit(arg)
811     void *arg;
812 {
813     Unacked *nacked = (Unacked *) arg;
814     int retval;
815
816 #if 1
817     syslog(LOG_DEBUG, "rexmit %s/%d #%d time %d",
818            inet_ntoa(nacked->dest.addr.sin_addr),
819            ntohs(nacked->dest.addr.sin_port), nacked->rexmits + 1, NOW);
820 #endif
821
822     nacked->rexmits++;
823     if (rexmit_times[nacked->rexmits] == -1) {
824         if (!nacked->client
825             || NOW - nacked->client->last_ack >= CLIENT_GIVEUP_MIN) {
826             /* The client (if there was one) has been unresponsive.
827              * Give up sending this packet, and kill the client if
828              * there was one.  (Make sure to remove nacked from the
829              * nack list before calling client_deregister(), which
830              * scans the nack list.)
831              */
832             LIST_DELETE(nacked);
833             if (nacked->client) {
834                 server_kill_clt(nacked->client);
835                 client_deregister(nacked->client, 1);
836             }
837             free(nacked->packet);
838             free(nacked);
839             return;
840         } else {
841             /* The client has sent us an ack recently.  Retry with the maximum
842              * retransmit time. */
843             nacked->rexmits--;
844         }
845     }
846
847     /* retransmit the packet */
848 #if 0
849     zdbug((LOG_DEBUG," to %s/%d", inet_ntoa(nacked->dest.addr.sin_addr),
850            ntohs(nacked->dest.addr.sin_port)));
851 #endif
852     retval = ZSetDestAddr(&nacked->dest.addr);
853     if (retval != ZERR_NONE) {
854         syslog(LOG_WARNING, "rexmit set addr: %s", error_message(retval));
855     } else {
856         retval = ZSendPacket(nacked->packet, nacked->packsz, 0);
857         if (retval != ZERR_NONE)
858             syslog(LOG_WARNING, "rexmit xmit: %s", error_message(retval));
859         if (retval == EAGAIN || retval == ENOBUFS)
860             nacked->rexmits--;
861     }
862
863     /* reset the timer */
864     nacked->timer = timer_set_rel(rexmit_times[nacked->rexmits], rexmit,
865                                   nacked);
866     return;
867 }
868
869 /*
870  * Send an acknowledgement to the sending client, by sending back the
871  * header from the original notice with the z_kind field changed to either
872  * SERVACK or SERVNAK, and the contents of the message either SENT or
873  * NOT_SENT, depending on the value of the sent argument.
874  */
875
876 void
877 clt_ack(notice, who, sent)
878     ZNotice_t *notice;
879     struct sockaddr_in *who;
880     Sent_type sent;
881 {
882     ZNotice_t acknotice;
883     ZPacket_t ackpack;
884     int packlen;
885     int notme = 0;
886     char *sent_name;
887     Code_t retval;
888
889     if (bdumping) {             /* don't ack while dumping */
890 #if 1
891         zdbug((LOG_DEBUG,"bdumping, no ack"));
892 #endif
893         return;
894     }
895
896     acknotice = *notice;
897
898     acknotice.z_kind = SERVACK;
899     switch (sent) {
900       case SENT:
901         acknotice.z_message = ZSRVACK_SENT;
902         sent_name = "sent";
903         break;
904       case NOT_FOUND:
905         acknotice.z_message = ZSRVACK_FAIL;
906         acknotice.z_kind = SERVNAK;
907         sent_name = "fail";
908         break;
909       case AUTH_FAILED:
910         acknotice.z_kind = SERVNAK;
911         acknotice.z_message = ZSRVACK_NOTSENT;
912         sent_name = "nak/not_sent";
913         break;
914       case NOT_SENT:
915         acknotice.z_message = ZSRVACK_NOTSENT;
916         sent_name = "not_sent";
917         break;
918       default:
919         abort ();
920     }
921
922 #if 0
923     zdbug((LOG_DEBUG,"clt_ack type %s for %d to %s/%d", sent_name,
924            ntohs(notice->z_port), inet_ntoa(who->sin_addr),
925            ntohs(who->sin_port)));
926 #endif
927
928     acknotice.z_multinotice = "";
929
930     /* leave room for the trailing null */
931     acknotice.z_message_len = strlen(acknotice.z_message) + 1;
932
933     packlen = sizeof(ackpack);
934
935     retval = ZFormatSmallRawNotice(&acknotice, ackpack, &packlen);
936
937     if (retval == ZERR_HEADERLEN) {
938       /* Since an ack header can be larger than a message header... (crock) */ 
939       acknotice.z_opcode = "";
940       acknotice.z_class = "";
941       acknotice.z_class_inst = "";
942       acknotice.z_opcode = "";
943       acknotice.z_default_format = "";
944
945       retval = ZFormatSmallRawNotice(&acknotice, ackpack, &packlen);
946     }
947
948     if (retval != ZERR_NONE) {
949         syslog(LOG_ERR, "clt_ack format: %s", error_message(retval));
950         return;
951     }
952     retval = ZSetDestAddr(who);
953     if (retval != ZERR_NONE) {
954         syslog(LOG_WARNING, "clt_ack set addr: %s", error_message(retval));
955         return;
956     }
957     retval = ZSendPacket(ackpack, packlen, 0);
958     if (retval != ZERR_NONE) {
959         syslog(LOG_WARNING, "clt_ack xmit: %s", error_message(retval));
960         return;
961     } else {
962         zdbug((LOG_DEBUG, "packet sent"));
963     }
964     return;
965 }
966
967 /*
968  * An ack has arrived.
969  * remove the packet matching this notice from the not-yet-acked queue
970  */
971
972 static void
973 nack_cancel(notice, who)
974     ZNotice_t *notice;
975     struct sockaddr_in *who;
976 {
977     Unacked *nacked;
978     int hashval;
979
980     /* search the not-yet-acked table for this packet, and flush it. */
981 #if 0
982     zdbug((LOG_DEBUG, "nack_cancel: %s:%08X,%08X",
983            inet_ntoa(notice->z_uid.zuid_addr),
984            notice->z_uid.tv.tv_sec, notice->z_uid.tv.tv_usec));
985 #endif
986     hashval = NACKTAB_HASHVAL(*who, notice->z_uid);
987     for (nacked = nacktab[hashval]; nacked; nacked = nacked->next) {
988         if (nacked->dest.addr.sin_addr.s_addr == who->sin_addr.s_addr
989             && nacked->dest.addr.sin_port == who->sin_port
990             && ZCompareUID(&nacked->uid, &notice->z_uid)) {
991             if (nacked->client)
992                 nacked->client->last_ack = NOW;
993             timer_reset(nacked->timer);
994             free(nacked->packet);
995             LIST_DELETE(nacked);
996             free(nacked);
997             return;
998         }
999     }
1000
1001 #if 1
1002     zdbug((LOG_DEBUG,"nack_cancel: nack not found %s:%08X,%08X",
1003            inet_ntoa (notice->z_uid.zuid_addr),
1004            notice->z_uid.tv.tv_sec, notice->z_uid.tv.tv_usec));
1005 #endif
1006 }
1007
1008 /* for compatibility when sending subscription information to old clients */
1009 #ifdef OLD_COMPAT
1010 #define OLD_ZEPHYR_VERSION      "ZEPH0.0"
1011 #endif /* OLD_COMPAT */
1012
1013 /* Dispatch an HM_CTL notice. */
1014
1015 Code_t
1016 hostm_dispatch(notice, auth, who, server)
1017     ZNotice_t *notice;
1018     int auth;
1019     struct sockaddr_in *who;
1020     Server *server;
1021 {
1022     Server *owner;
1023     char *opcode = notice->z_opcode;
1024     Code_t retval;
1025     int i, add = 0, remove = 0;
1026
1027 #if 0
1028     zdbug((LOG_DEBUG,"hm_disp"));
1029 #endif
1030
1031     if (notice->z_kind == HMACK) {
1032         /* Ignore. */
1033         ;
1034     } else if (notice->z_kind != HMCTL) {
1035 #if 0
1036         zdbug((LOG_DEBUG, "bogus HM packet"));
1037 #endif
1038         clt_ack(notice, who, AUTH_FAILED);
1039     } else if (strcmp(opcode, HM_FLUSH) == 0) {
1040         client_flush_host(&who->sin_addr);
1041         if (server == me_server)
1042             server_forward(notice, auth, who);
1043     } else if (strcmp(opcode, HM_BOOT) == 0) {
1044         client_flush_host(&who->sin_addr);
1045         if (server == me_server) {
1046             server_forward(notice, auth, who);
1047             ack(notice, who);
1048             add = 1;
1049         }
1050     } else if (strcmp(opcode, HM_ATTACH) == 0) {
1051         if (server == me_server) {
1052             server_forward(notice, auth, who);
1053             ack(notice, who);
1054             add = 1;
1055         } else {
1056             remove = 1;
1057         }
1058     } else if (strcmp(opcode, HM_DETACH) == 0) {
1059         remove = 1;
1060     } else {
1061         syslog(LOG_WARNING, "hm_dispatch: unknown opcode %s", opcode);
1062     }
1063
1064     if (add) {
1065         for (i = 0; i < num_hosts; i++) {
1066             if (hosts[i].s_addr == who->sin_addr.s_addr)
1067                 break;
1068         }
1069         if (i == num_hosts) {
1070             if (hosts_size == 0) {
1071                 hosts = (struct in_addr *) malloc(HOSTS_SIZE_INIT *
1072                                                   sizeof(struct in_addr));
1073                 if (!hosts)
1074                     return ENOMEM;
1075                 hosts_size = HOSTS_SIZE_INIT;
1076             } else if (num_hosts == hosts_size) {
1077                 hosts = (struct in_addr *) realloc(hosts, hosts_size * 2 *
1078                                                    sizeof(struct in_addr));
1079                 if (!hosts)
1080                     return ENOMEM;
1081                 hosts_size *= 2;
1082             }
1083             hosts[num_hosts++] = who->sin_addr;
1084         }
1085     } else if (remove) {
1086         for (i = 0; i < num_hosts; i++) {
1087             if (hosts[i].s_addr == who->sin_addr.s_addr) {
1088                 memmove(&hosts[i], &hosts[i + 1], num_hosts - (i + 1));
1089                 num_hosts--;
1090                 break;
1091             }
1092         }
1093     }
1094     return ZERR_NONE;
1095 }
1096
1097 /*
1098  * Dispatch a ZEPHYR_CTL notice.
1099  */
1100
1101 Code_t
1102 control_dispatch(notice, auth, who, server)
1103     ZNotice_t *notice;
1104     int auth;
1105     struct sockaddr_in *who;
1106     Server *server;
1107 {
1108     char *opcode = notice->z_opcode;
1109     Client *client;
1110     Code_t retval;
1111     int wantdefs;
1112     ZRealm *realm;
1113     struct sockaddr_in newwho;
1114
1115     /*
1116      * ZEPHYR_CTL Opcodes expected are:
1117      *  BOOT (inst HM): host has booted; flush data.
1118      *  CLIENT_SUBSCRIBE: process with the subscription mananger.
1119      *  CLIENT_UNSUBSCRIBE: ""
1120      *  CLIENT_CANCELSUB:   ""
1121      */
1122
1123     zdbug((LOG_DEBUG, "ctl_disp: opc=%s", opcode));
1124
1125     newwho.sin_addr.s_addr = notice->z_sender_addr.s_addr;
1126     newwho.sin_port = notice->z_port;
1127     realm = realm_which_realm(&newwho);
1128     if (realm)
1129         return(realm_control_dispatch(notice, auth, who, server, realm));
1130
1131     if (strcasecmp(notice->z_class_inst, ZEPHYR_CTL_HM) == 0) {
1132         return hostm_dispatch(notice, auth, who, server);
1133     } else if (strcmp(opcode, CLIENT_GIMMESUBS) == 0 ||
1134                strcmp(opcode, CLIENT_GIMMEDEFS) == 0) {
1135         /* this special case is before the auth check so that
1136            someone who has no subscriptions does NOT get a SERVNAK
1137            but rather an empty list.  Note we must therefore
1138            check authentication inside subscr_sendlist */
1139 #ifdef OLD_COMPAT
1140         /* only acknowledge if *not* old version; the old version
1141            acknowledges the packet with the reply */
1142         if (strcmp(notice->z_version, OLD_ZEPHYR_VERSION) != 0)
1143             ack(notice, who);
1144 #else /* !OLD_COMPAT */
1145         ack(notice, who);
1146 #endif /* OLD_COMPAT */
1147         subscr_sendlist(notice, auth, who);
1148         return ZERR_NONE;
1149     } else if (!auth) {
1150 #if 0
1151         zdbug((LOG_DEBUG,"unauth ctrl_disp"));
1152 #endif
1153         if (server == me_server)
1154             clt_ack(notice, who, AUTH_FAILED);
1155         return ZERR_NONE;
1156     }
1157
1158     wantdefs = strcmp(opcode, CLIENT_SUBSCRIBE_NODEFS);
1159     if (!wantdefs || strcmp(opcode, CLIENT_SUBSCRIBE) == 0) {
1160         /* subscription notice */
1161         retval = client_register(notice, &who->sin_addr, &client, wantdefs);
1162         if (retval != ZERR_NONE) {
1163             syslog(LOG_NOTICE, "subscr %s/%s/%d failed: %s",
1164                    notice->z_sender, inet_ntoa(who->sin_addr),
1165                    ntohs(notice->z_port), error_message(retval));
1166             if (server == me_server) {
1167                 if (retval == ZSRV_BADSUBPORT)
1168                     clt_ack(notice, who, AUTH_FAILED);
1169                 else
1170                     nack(notice, who);
1171             }
1172             return(ZERR_NONE);
1173         }
1174         if (strcmp(client->principal->string, notice->z_sender) != 0) {
1175             /* you may only subscribe for your own clients */
1176             if (server == me_server)
1177                 clt_ack(notice, who, AUTH_FAILED);
1178             return ZERR_NONE;
1179         }
1180 #ifdef HAVE_KRB5
1181         if (client->session_keyblock) {
1182              krb5_free_keyblock_contents(Z_krb5_ctx, client->session_keyblock);
1183              retval = krb5_copy_keyblock_contents(Z_krb5_ctx, ZGetSession(),
1184                                          client->session_keyblock);
1185         } else {
1186              retval = krb5_copy_keyblock(Z_krb5_ctx, ZGetSession(), 
1187                                 &client->session_keyblock);
1188         }
1189         if (retval) {
1190              syslog(LOG_WARNING, "keyblock copy failed in subscr: %s",
1191                     error_message(retval));
1192              if (server == me_server)
1193                   nack(notice, who);
1194              return ZERR_NONE;
1195         }
1196 #else
1197 #ifdef HAVE_KRB4
1198         /* in case it's changed */
1199         memcpy(client->session_key, ZGetSession(), sizeof(C_Block));
1200 #endif
1201 #endif
1202         retval = subscr_subscribe(client, notice, server);
1203         if (retval != ZERR_NONE) {
1204             syslog(LOG_WARNING, "subscr failed: %s", error_message(retval));
1205             if (server == me_server)
1206                 nack(notice, who);
1207             return ZERR_NONE;
1208         }
1209     } else if (strcmp(opcode, CLIENT_UNSUBSCRIBE) == 0) {
1210         client = client_find(&who->sin_addr, notice->z_port);
1211         if (client != NULL) {
1212             if (strcmp(client->principal->string, notice->z_sender) != 0) {
1213                 /* you may only cancel for your own clients */
1214                 if (server == me_server)
1215                     clt_ack(notice, who, AUTH_FAILED);
1216                 return ZERR_NONE;
1217             }
1218 #if 0
1219             if (zdebug) {
1220                 if (server == me_server) {
1221                     syslog(LOG_DEBUG, "subscription cancel for %s/%d\n",
1222                            inet_ntoa(who->sin_addr), ntohs(who->sin_port));
1223                 } else {
1224                     syslog(LOG_DEBUG,
1225                            "subscription cancel for %s/%d from %s\n",
1226                            inet_ntoa(who->sin_addr), ntohs(who->sin_port),
1227                            server->addr_str);
1228                 }
1229             }
1230 #endif
1231             subscr_cancel(who, notice);
1232         } else {
1233             nack(notice, who);
1234             return ZERR_NONE;
1235         }
1236     } else if (strcmp(opcode, CLIENT_CANCELSUB) == 0) {
1237         /* canceling subscriptions implies I can punt info about this client */
1238         client = client_find(&who->sin_addr, notice->z_port);
1239         if (client == NULL) {
1240 #if 0
1241             zdbug((LOG_DEBUG,"can_sub not found client"));
1242 #endif
1243             if (server == me_server)
1244                 nack(notice, who);
1245             return ZERR_NONE;
1246         }
1247         if (strcmp(client->principal->string, notice->z_sender) != 0) {
1248             /* you may only cancel for your own clients */
1249             if (server == me_server)
1250                 clt_ack(notice, who, AUTH_FAILED);
1251             return ZERR_NONE;
1252         }
1253         /* don't flush locations here, let him do it explicitly */
1254 #if 0
1255         zdbug((LOG_DEBUG, "cancelsub clt_dereg %s/%d",
1256                inet_ntoa(who->sin_addr), ntohs(who->sin_port)));
1257 #endif
1258         client_deregister(client, 0);
1259     } else {
1260         syslog(LOG_WARNING, "unknown ctl opcode %s", opcode); 
1261         if (server == me_server) {
1262             if (strcmp(notice->z_class_inst, ZEPHYR_CTL_REALM) != 0)
1263                 nack(notice, who);
1264         }
1265         return ZERR_NONE;
1266     }
1267
1268     if (server == me_server) {
1269         ack(notice, who);
1270         server_forward(notice, auth, who);
1271     }
1272     return ZERR_NONE;
1273 }
1274
1275 void
1276 hostm_shutdown()
1277 {
1278     int i, s, newserver;
1279     struct sockaddr_in sin;
1280
1281     for (i = 0; i < nservers; i++) {
1282         if (i != me_server_idx && otherservers[i].state == SERV_UP)
1283             break;
1284     }
1285     newserver = (i < nservers);
1286     for (i = 0; i < num_hosts; i++) {
1287         sin.sin_addr = hosts[i];
1288         sin.sin_port = hm_port;
1289         if (newserver) {
1290             while (1) {
1291                 s = (random() % (nservers - 1)) + 1;
1292                 if (otherservers[s].state == SERV_UP)
1293                     break;
1294             }
1295             hostm_deathgram(&sin, &otherservers[s]);
1296         } else {
1297             hostm_deathgram(&sin, NULL);
1298         }
1299     }
1300 }
1301
1302 void
1303 realm_shutdown()
1304 {
1305     int i, s, newserver;
1306     struct sockaddr_in sin;
1307
1308     for (i = 0; i < nservers; i++) {
1309         if (i != me_server_idx && otherservers[i].state == SERV_UP)
1310             break;
1311     }
1312     zdbug((LOG_DEBUG, "rlm_shutdown"));
1313
1314     newserver = (i < nservers);
1315     if (newserver) {
1316       while (1) {
1317         s = (random() % (nservers - 1)) + 1;
1318         if (otherservers[s].state == SERV_UP)
1319           break;
1320       }
1321       realm_deathgram(&otherservers[s]);
1322     } else {
1323       realm_deathgram(NULL);
1324     }
1325 }
1326
1327 static void
1328 hostm_deathgram(sin, server)
1329     struct sockaddr_in *sin;
1330     Server *server;
1331 {
1332     Code_t retval;
1333     int shutlen;
1334     ZNotice_t shutnotice;
1335     char *shutpack;
1336
1337     memset (&shutnotice, 0, sizeof(shutnotice));
1338
1339     shutnotice.z_kind = HMCTL;
1340     shutnotice.z_port = sin->sin_port; /* we are sending it */
1341     shutnotice.z_class = HM_CTL_CLASS;
1342     shutnotice.z_class_inst = HM_CTL_SERVER;
1343     shutnotice.z_opcode = SERVER_SHUTDOWN;
1344     shutnotice.z_sender = HM_CTL_SERVER;
1345     shutnotice.z_recipient = hm_recipient();
1346     shutnotice.z_default_format = "";
1347     shutnotice.z_num_other_fields = 0;
1348     shutnotice.z_message = (server) ? server->addr_str : NULL;
1349     shutnotice.z_message_len = (server) ? strlen(server->addr_str) + 1 : 0;
1350
1351     retval = ZFormatNotice(&shutnotice, &shutpack, &shutlen, ZNOAUTH);
1352     if (retval != ZERR_NONE) {
1353         syslog(LOG_ERR, "hm_shut format: %s",error_message(retval));
1354         return;
1355     }
1356     retval = ZSetDestAddr(sin);
1357     if (retval != ZERR_NONE) {
1358         syslog(LOG_WARNING, "hm_shut set addr: %s", error_message(retval));
1359         free(shutpack);
1360         return;
1361     }
1362     retval = ZSendPacket(shutpack, shutlen, 0);
1363     if (retval != ZERR_NONE)
1364         syslog(LOG_WARNING, "hm_shut xmit: %s", error_message(retval));
1365     free(shutpack);
1366 }
1367
1368 static char *
1369 hm_recipient()
1370 {
1371     static char *recipient;
1372     char *realm;
1373
1374     if (recipient)
1375         return recipient;
1376
1377     realm = ZGetRealm();
1378     if (!realm)
1379         realm = "???";
1380     recipient = (char *) malloc(strlen(realm) + 4);
1381     strcpy (recipient, "hm@");
1382     strcat (recipient, realm);
1383     return recipient;
1384 }
1385