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