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