]> asedeno.scripts.mit.edu Git - 1ts-debian.git/blob - zephyr/server/dispatch.c
krb5-interrealm patches
[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: ghudson $
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
18 #ifndef lint
19 #ifndef SABER
20 static const char rcsid_dispatch_c[] =
21 "$Id: dispatch.c,v 1.63 2001/04/10 23:28:19 ghudson Exp $";
22 #endif
23 #endif
24
25 #define NACKTAB_HASHSIZE                1023
26 #define NACKTAB_HASHVAL(sockaddr, uid)  (((sockaddr).sin_addr.s_addr ^ \
27                                           (sockaddr).sin_port ^ \
28                                           (uid).zuid_addr.s_addr ^ \
29                                           (uid).tv.tv_sec ^ \
30                                           (uid).tv.tv_usec) % NACKTAB_HASHSIZE)
31 #define HOSTS_SIZE_INIT                 256
32
33 #ifdef DEBUG
34 ZCONST char *ZNoticeKinds[9] = {"UNSAFE", "UNACKED", "ACKED", "HMACK",
35                                     "HMCTL", "SERVACK", "SERVNAK", "CLIENTACK",
36                                     "STAT"};
37 #endif
38 /*
39  *
40  * External Routines:
41  *
42  * void dispatch(notice, auth, who)
43  *      ZNotice_t *notice;
44  *      int auth;
45  *      struct sockaddr_in *who;
46  *
47  * void clt_ack(notice, who, sent)
48  *      ZNotice_t *notice;
49  *      struct sockaddr_in *who;
50  *      Sent_type sent;
51  *
52  * void nack_release(client)
53  *      Client *client;
54  *
55  * void sendit(notice, auth, who, external)
56  *      ZNotice_t *notice;
57  *      int auth;
58  *      struct sockaddr_in *who;
59  *      int external;
60  *
61  * void xmit(notice, dest, auth, client)
62  *      ZNotice_t *notice;
63  *      struct sockaddr_in *dest;
64  *      int auth;
65  *      Client *client;
66  */
67
68
69 String *class_control, *class_admin, *class_hm, *class_ulogin, *class_ulocate;
70
71 int rexmit_times[] = REXMIT_TIMES;
72
73 static void nack_cancel __P((ZNotice_t *, struct sockaddr_in *));
74 static void dispatch __P((ZNotice_t *, int, struct sockaddr_in *, int));
75 static int send_to_dest __P((ZNotice_t *, int, Destination *dest, int, int));
76 static void hostm_deathgram __P((struct sockaddr_in *, Server *));
77 static char *hm_recipient __P((void));
78
79 Statistic realm_notices = {0, "inter-realm notices"};
80 Statistic interserver_notices = {0, "inter-server notices"};
81 Statistic hm_packets = {0, "hostmanager packets"};
82 Statistic control_notices = {0, "client control notices"};
83 Statistic message_notices = {0, "message notices"};
84 Statistic login_notices = {0, "login notices"};
85 Statistic i_s_ctls = {0, "inter-server control notices"};
86 Statistic i_s_logins = {0, "inter-server login notices"};
87 Statistic i_s_admins = {0, "inter-server admin notices"};
88 Statistic i_s_locates = {0, "inter-server locate notices"};
89 Statistic locate_notices = {0, "locate notices"};
90 Statistic admin_notices = {0, "admin notices"};
91
92 static Unacked *nacktab[NACKTAB_HASHSIZE];
93 static struct in_addr *hosts;
94 static int hosts_size = 0, num_hosts = 0;
95
96 static void
97 dump_stats (arg)
98     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()
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(notice, auth, who, from_server)
247     ZNotice_t *notice;
248     int auth;
249     struct sockaddr_in *who;
250     int from_server;
251 {
252     Code_t status;
253     String *notice_class;
254     struct sockaddr_in who2;
255     int authflag;
256     ZRealm *realm;
257     char *cp;
258 #ifdef DEBUG
259     char dbg_buf[BUFSIZ];
260 #endif
261
262     authflag = (auth == ZAUTH_YES);
263
264     if ((int) notice->z_kind < (int) UNSAFE ||
265         (int) notice->z_kind > (int) CLIENTACK) {
266         syslog(LOG_NOTICE, "bad notice kind 0x%x from %s", notice->z_kind,
267                inet_ntoa(who->sin_addr));
268         return;
269     }
270 #if 0
271     if (zdebug) {
272         syslog(LOG_DEBUG,
273                 "disp:%s '%s' '%s' '%s' notice to '%s' from '%s' %s/%d/%d",
274                 ZNoticeKinds[(int) notice->z_kind], notice->z_class,
275                 notice->z_class_inst, notice->z_opcode, notice->z_recipient,
276                 notice->z_sender, inet_ntoa(who->sin_addr),
277                 ntohs(who->sin_port), ntohs(notice->z_port));
278     }
279 #endif
280
281     if (notice->z_kind == CLIENTACK) {
282         nack_cancel(notice, who);
283         return;
284     }
285
286     who2 = *who;
287 #if 0
288     if (0 && from_server) {
289         /* incorporate server_dispatch here */
290     }
291 #endif
292     notice_class = make_string(notice->z_class,1);
293
294     if (from_server) {
295         interserver_notices.val++;
296         status = server_dispatch(notice, authflag, who);
297     } else if (class_is_hm(notice_class)) {
298         hm_packets.val++;
299         status = hostm_dispatch(notice, authflag, who, me_server);
300     } else if (realm_which_realm(who) && !(class_is_admin(notice_class))) {
301         realm_notices.val++;
302         status = realm_dispatch(notice, authflag, who, me_server);
303     } else if (class_is_control(notice_class)) {
304         control_notices.val++;
305         status = control_dispatch(notice, authflag, who, me_server);
306     } else if (class_is_ulogin(notice_class)) {
307         login_notices.val++;
308         status = ulogin_dispatch(notice, authflag, who, me_server);
309     } else if (class_is_ulocate(notice_class)) {
310         locate_notices.val++;
311         status = ulocate_dispatch(notice, authflag, who, me_server);
312     } else if (class_is_admin(notice_class)) {
313         admin_notices.val++;
314         status = server_adispatch(notice, authflag, who, me_server);
315     } else {
316         if (!realm_bound_for_realm(ZGetRealm(), notice->z_recipient)) {
317             cp = strchr(notice->z_recipient, '@');
318             if (!cp ||
319                 !(realm = realm_get_realm_by_name(cp + 1))) {
320                 /* Foreign user, local realm */
321                 sendit(notice, authflag, who, 0);
322             } else
323                 realm_handoff(notice, authflag, who, realm, 1);
324         } else {
325             if (notice->z_recipient[0] == '@')
326                 notice->z_recipient = "";
327             sendit(notice, authflag, who, 1);
328         }
329         free_string(notice_class);
330         return;
331     }
332
333     if (status == ZSRV_REQUEUE)
334         server_self_queue(notice, authflag, who);
335     free_string(notice_class);
336 }
337
338 /*
339  * Send a notice off to those clients who have subscribed to it.
340  */
341
342 void
343 sendit(notice, auth, who, external)
344     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(notice, auth, dest, send_counter, external)
477     ZNotice_t *notice;
478     int auth;
479     Destination *dest;
480     int send_counter;
481     int external;
482 {
483     Client **clientp;
484     int any = 0;
485
486     clientp = triplet_lookup(dest);
487     if (!clientp)
488         return 0;
489
490     for (; *clientp; clientp++) {
491         if ((*clientp)->last_send == send_counter)
492             continue;
493         (*clientp)->last_send = send_counter;
494         if ((*clientp)->realm) {
495           if (external) {
496             realm_handoff(notice, auth, &clientp[0]->addr, clientp[0]->realm,
497                           1);
498             any = 1;
499           }
500         } else {
501             xmit(notice, &((*clientp)->addr), auth, *clientp);
502             any = 1;
503         }
504     }
505
506     return any;
507 }
508
509 /*
510  * Release anything destined for the client in the not-yet-acked table.
511  */
512
513 void
514 nack_release(client)
515     Client *client;
516 {
517     int i;
518     Unacked *nacked, *next;
519
520     for (i = 0; i < NACKTAB_HASHSIZE; i++) {
521         for (nacked = nacktab[i]; nacked; nacked = next) {
522             next = nacked->next;
523             if (nacked->client == client) {
524                 timer_reset(nacked->timer);
525                 LIST_DELETE(nacked);
526                 free(nacked->packet);
527                 free(nacked);
528             }
529         }
530     }
531 }
532
533 /*
534  * Send one packet of a fragmented message to a client.  After transmitting,
535  * put it onto the not ack'ed list.
536  */
537
538 /* the arguments must be the same as the arguments to Z_XmitFragment */
539 /*ARGSUSED*/
540 Code_t
541 xmit_frag(notice, buf, len, waitforack)
542     ZNotice_t *notice;
543     char *buf;
544     int len;
545     int waitforack;
546 {
547     struct sockaddr_in sin;
548     char *savebuf;
549     Unacked *nacked;
550     Code_t retval;
551     int hashval, sendfail = 0;
552
553     retval = ZSendPacket(buf, len, 0);
554     if (retval != ZERR_NONE) {
555         syslog(LOG_WARNING, "xmit_frag send: %s", error_message(retval));
556         if (retval != EAGAIN && retval != ENOBUFS)
557             return retval;
558         sendfail = 1;
559     }
560
561     /* now we've sent it, mark it as not ack'ed */
562     nacked = (Unacked *) malloc(sizeof(Unacked));
563     if (!nacked) {
564         /* no space: just punt */
565         syslog(LOG_WARNING, "xmit_frag nack malloc");
566         return ENOMEM;
567     }
568
569     savebuf = (char *) malloc(len);
570     if (!savebuf) {
571         /* no space: just punt */
572         syslog(LOG_WARNING, "xmit_frag pack malloc");
573         free(nacked);
574         return ENOMEM;
575     }
576
577     memcpy(savebuf, buf, len);
578
579     sin = ZGetDestAddr();
580     nacked->client = NULL;
581     nacked->rexmits = (sendfail) ? -1 : 0;
582     nacked->packet = savebuf;
583     nacked->dest.addr = sin;
584     nacked->packsz = len;
585     nacked->uid = notice->z_uid;
586     nacked->timer = timer_set_rel(rexmit_times[0], rexmit, nacked);
587     LIST_INSERT(&nacktab[NACKTAB_HASHVAL(sin, nacked->uid)], nacked);
588     return(ZERR_NONE);
589 }
590
591 /*
592  * Send the notice to the client.  After transmitting, put it onto the
593  * not ack'ed list.
594  */
595
596 void
597 xmit(notice, dest, auth, client)
598     ZNotice_t *notice;
599     struct sockaddr_in *dest;
600     int auth;
601     Client *client;
602 {
603     char *noticepack;
604     Unacked *nacked;
605     int packlen, sendfail = 0;
606     Code_t retval;
607
608 #if 0
609     zdbug((LOG_DEBUG,"xmit"));
610 #endif
611
612     noticepack = (char *) malloc(sizeof(ZPacket_t));
613     if (!noticepack) {
614         syslog(LOG_ERR, "xmit malloc");
615         return;                 /* DON'T put on nack list */
616     }
617         
618     packlen = sizeof(ZPacket_t);
619
620     if (auth && client) {       /*
621                                    we are distributing authentic and
622                                    we have a pointer to auth info
623                                    */
624 #ifdef HAVE_KRB4
625         retval = ZFormatAuthenticNotice(notice, noticepack, packlen, &packlen,
626                                         client->session_key);
627         if (retval != ZERR_NONE) {
628             syslog(LOG_ERR, "xmit auth format: %s", error_message(retval));
629             free(noticepack);
630             return;
631         }
632 #else /* !HAVE_KRB4 */
633         notice->z_auth = 1;
634         retval = ZFormatSmallRawNotice(notice, noticepack, &packlen);
635         if (retval != ZERR_NONE) {
636             syslog(LOG_ERR, "xmit auth/raw format: %s", error_message(retval));
637             free(noticepack);
638             return;
639         }
640 #endif /* HAVE_KRB4 */
641     } else {
642         notice->z_auth = 0;
643         notice->z_authent_len = 0;
644         notice->z_ascii_authent = (char *)"";
645         retval = ZFormatSmallRawNotice(notice, noticepack, &packlen);
646         /* This code is needed because a Zephyr can "grow" when a remote 
647          * realm name is inserted into the Zephyr before being resent out
648          * locally. It essentially matches the code in realm.c to do the
649          * same thing with authentic Zephyrs.
650          */
651         if (retval == ZERR_PKTLEN) {
652           ZNotice_t partnotice, newnotice;
653           char multi[64];
654           char *buffer, *ptr;
655           int buffer_len, hdrlen, offset, fragsize, ret_len, message_len;
656           int origoffset, origlen;
657
658           free(noticepack);
659
660           retval = ZSetDestAddr(dest);
661           if (retval != ZERR_NONE) {
662             syslog(LOG_WARNING, "xmit set addr: %s", error_message(retval));
663             return;
664           }
665
666           partnotice = *notice;
667           
668           partnotice.z_auth = 0;
669           partnotice.z_authent_len = 0;
670           partnotice.z_ascii_authent = (char *)"";
671
672           origoffset = offset = fragsize = 0;
673           origlen = notice->z_message_len;
674
675           buffer = (char *) malloc(sizeof(ZPacket_t));
676           if (!buffer) {
677             syslog(LOG_ERR, "xmit unauth refrag malloc");
678             return;                 /* DON'T put on nack list */
679           }
680           buffer_len = sizeof(ZPacket_t);
681
682           retval = Z_FormatRawHeader(&partnotice, buffer, buffer_len, 
683                                      &hdrlen, NULL, NULL);
684           if (retval != ZERR_NONE) {
685             syslog(LOG_ERR, "xmit unauth refrag fmt: failed");
686             free(buffer);
687             return;
688           }
689
690           if (notice->z_multinotice && strcmp(notice->z_multinotice, ""))
691             if (sscanf(notice->z_multinotice, "%d/%d", &origoffset, &origlen)
692                 != 2) 
693               {
694                 syslog(LOG_WARNING, "xmit unauth refrag: parse failed");
695                 free(buffer);
696                 return;
697               }
698
699               fragsize = Z_MAXPKTLEN-hdrlen-Z_FRAGFUDGE;
700
701           while (offset < notice->z_message_len || !notice->z_message_len) {
702             (void) sprintf(multi, "%d/%d", offset+origoffset, origlen);
703             partnotice.z_multinotice = multi;
704             if (offset > 0) {
705               (void) gettimeofday(&partnotice.z_uid.tv, (struct timezone *)0);
706               partnotice.z_uid.tv.tv_sec = htonl((u_long)
707                                                  partnotice.z_uid.tv.tv_sec);
708               partnotice.z_uid.tv.tv_usec = htonl((u_long)
709                                                   partnotice.z_uid.tv.tv_usec);
710               (void) memcpy((char *)&partnotice.z_uid.zuid_addr, &__My_addr,
711                             sizeof(__My_addr));
712             }
713             partnotice.z_message = notice->z_message+offset;
714             message_len = min(notice->z_message_len-offset, fragsize);
715             partnotice.z_message_len = message_len;
716
717             retval = Z_FormatRawHeader(&partnotice, buffer, buffer_len, 
718                                        &hdrlen, &ptr, NULL);
719             if (retval != ZERR_NONE) {
720               syslog(LOG_WARNING, "xmit unauth refrag raw: %s",
721                      error_message(retval));
722               free(buffer);
723               return;
724             }
725
726             ptr = buffer+hdrlen;
727
728             (void) memcpy(ptr, partnotice.z_message, partnotice.z_message_len);
729             
730             buffer_len = hdrlen+partnotice.z_message_len;
731
732             xmit_frag(&partnotice, buffer, buffer_len, 0);
733
734             offset += fragsize;
735
736             if (!notice->z_message_len)
737               break;
738           }
739           free(buffer);
740           return;
741         }
742         /* End of refrag code */
743
744         if (retval != ZERR_NONE) {
745             syslog(LOG_ERR, "xmit format: %s", error_message(retval));
746             free(noticepack);
747             return;                     /* DON'T put on nack list */
748         }
749     }
750 #if 0
751     zdbug((LOG_DEBUG," to %s/%d", inet_ntoa(dest->sin_addr),
752            ntohs(dest->sin_port)));
753 #endif
754     retval = ZSetDestAddr(dest);
755     if (retval != ZERR_NONE) {
756         syslog(LOG_WARNING, "xmit set addr: %s", error_message(retval));
757         free(noticepack);
758         return;
759     }
760     retval = ZSendPacket(noticepack, packlen, 0);
761     if (retval != ZERR_NONE) {
762         syslog(LOG_WARNING, "xmit xmit: (%s/%d) %s", inet_ntoa(dest->sin_addr),
763                ntohs(dest->sin_port), error_message(retval));
764         if (retval != EAGAIN && retval != ENOBUFS) {
765             free(noticepack);
766             return;
767         }
768         sendfail = 1;
769     }
770
771     /* now we've sent it, mark it as not ack'ed */
772
773     nacked = (Unacked *) malloc(sizeof(Unacked));
774     if (!nacked) {
775         /* no space: just punt */
776         syslog(LOG_WARNING, "xmit nack malloc");
777         free(noticepack);
778         return;
779     }
780
781     nacked->client = client;
782     nacked->rexmits = (sendfail) ? -1 : 0;
783     nacked->packet = noticepack;
784     nacked->dest.addr = *dest;
785     nacked->packsz = packlen;
786     nacked->uid = notice->z_uid;
787     nacked->timer = timer_set_rel(rexmit_times[0], rexmit, nacked);
788     LIST_INSERT(&nacktab[NACKTAB_HASHVAL(*dest, nacked->uid)], nacked);
789 }
790
791 /*
792  * Retransmit the packet specified.  If we have timed out or retransmitted
793  * too many times, punt the packet and initiate the host recovery algorithm
794  * Else, increment the count and re-send the notice packet.
795  */
796
797 void
798 rexmit(arg)
799     void *arg;
800 {
801     Unacked *nacked = (Unacked *) arg;
802     int retval;
803
804 #if 1
805     syslog(LOG_DEBUG, "rexmit %s/%d #%d time %d",
806            inet_ntoa(nacked->dest.addr.sin_addr),
807            ntohs(nacked->dest.addr.sin_port), nacked->rexmits + 1, NOW);
808 #endif
809
810     nacked->rexmits++;
811     if (rexmit_times[nacked->rexmits] == -1) {
812         if (!nacked->client
813             || NOW - nacked->client->last_ack >= CLIENT_GIVEUP_MIN) {
814             /* The client (if there was one) has been unresponsive.
815              * Give up sending this packet, and kill the client if
816              * there was one.  (Make sure to remove nacked from the
817              * nack list before calling client_deregister(), which
818              * scans the nack list.)
819              */
820             LIST_DELETE(nacked);
821             if (nacked->client) {
822                 server_kill_clt(nacked->client);
823                 client_deregister(nacked->client, 1);
824             }
825             free(nacked->packet);
826             free(nacked);
827             return;
828         } else {
829             /* The client has sent us an ack recently.  Retry with the maximum
830              * retransmit time. */
831             nacked->rexmits--;
832         }
833     }
834
835     /* retransmit the packet */
836 #if 0
837     zdbug((LOG_DEBUG," to %s/%d", inet_ntoa(nacked->dest.addr.sin_addr),
838            ntohs(nacked->dest.addr.sin_port)));
839 #endif
840     retval = ZSetDestAddr(&nacked->dest.addr);
841     if (retval != ZERR_NONE) {
842         syslog(LOG_WARNING, "rexmit set addr: %s", error_message(retval));
843     } else {
844         retval = ZSendPacket(nacked->packet, nacked->packsz, 0);
845         if (retval != ZERR_NONE)
846             syslog(LOG_WARNING, "rexmit xmit: %s", error_message(retval));
847         if (retval == EAGAIN || retval == ENOBUFS)
848             nacked->rexmits--;
849     }
850
851     /* reset the timer */
852     nacked->timer = timer_set_rel(rexmit_times[nacked->rexmits], rexmit,
853                                   nacked);
854     return;
855 }
856
857 /*
858  * Send an acknowledgement to the sending client, by sending back the
859  * header from the original notice with the z_kind field changed to either
860  * SERVACK or SERVNAK, and the contents of the message either SENT or
861  * NOT_SENT, depending on the value of the sent argument.
862  */
863
864 void
865 clt_ack(notice, who, sent)
866     ZNotice_t *notice;
867     struct sockaddr_in *who;
868     Sent_type sent;
869 {
870     ZNotice_t acknotice;
871     ZPacket_t ackpack;
872     int packlen;
873     int notme = 0;
874     char *sent_name;
875     Code_t retval;
876
877     if (bdumping) {             /* don't ack while dumping */
878 #if 1
879         zdbug((LOG_DEBUG,"bdumping, no ack"));
880 #endif
881         return;
882     }
883
884     acknotice = *notice;
885
886     acknotice.z_kind = SERVACK;
887     switch (sent) {
888       case SENT:
889         acknotice.z_message = ZSRVACK_SENT;
890         sent_name = "sent";
891         break;
892       case NOT_FOUND:
893         acknotice.z_message = ZSRVACK_FAIL;
894         acknotice.z_kind = SERVNAK;
895         sent_name = "fail";
896         break;
897       case AUTH_FAILED:
898         acknotice.z_kind = SERVNAK;
899         acknotice.z_message = ZSRVACK_NOTSENT;
900         sent_name = "nak/not_sent";
901         break;
902       case NOT_SENT:
903         acknotice.z_message = ZSRVACK_NOTSENT;
904         sent_name = "not_sent";
905         break;
906       default:
907         abort ();
908     }
909
910 #if 0
911     zdbug((LOG_DEBUG,"clt_ack type %s for %d to %s/%d", sent_name,
912            ntohs(notice->z_port), inet_ntoa(who->sin_addr),
913            ntohs(who->sin_port)));
914 #endif
915
916     acknotice.z_multinotice = "";
917
918     /* leave room for the trailing null */
919     acknotice.z_message_len = strlen(acknotice.z_message) + 1;
920
921     packlen = sizeof(ackpack);
922
923     retval = ZFormatSmallRawNotice(&acknotice, ackpack, &packlen);
924
925     if (retval == ZERR_HEADERLEN) {
926       /* Since an ack header can be larger than a message header... (crock) */ 
927       acknotice.z_opcode = "";
928       acknotice.z_class = "";
929       acknotice.z_class_inst = "";
930       acknotice.z_opcode = "";
931       acknotice.z_default_format = "";
932
933       retval = ZFormatSmallRawNotice(&acknotice, ackpack, &packlen);
934     }
935
936     if (retval != ZERR_NONE) {
937         syslog(LOG_ERR, "clt_ack format: %s", error_message(retval));
938         return;
939     }
940     retval = ZSetDestAddr(who);
941     if (retval != ZERR_NONE) {
942         syslog(LOG_WARNING, "clt_ack set addr: %s", error_message(retval));
943         return;
944     }
945     retval = ZSendPacket(ackpack, packlen, 0);
946     if (retval != ZERR_NONE) {
947         syslog(LOG_WARNING, "clt_ack xmit: %s", error_message(retval));
948         return;
949     } else {
950         zdbug((LOG_DEBUG, "packet sent"));
951     }
952     return;
953 }
954
955 /*
956  * An ack has arrived.
957  * remove the packet matching this notice from the not-yet-acked queue
958  */
959
960 static void
961 nack_cancel(notice, who)
962     ZNotice_t *notice;
963     struct sockaddr_in *who;
964 {
965     Unacked *nacked;
966     int hashval;
967
968     /* search the not-yet-acked table for this packet, and flush it. */
969 #if 0
970     zdbug((LOG_DEBUG, "nack_cancel: %s:%08X,%08X",
971            inet_ntoa(notice->z_uid.zuid_addr),
972            notice->z_uid.tv.tv_sec, notice->z_uid.tv.tv_usec));
973 #endif
974     hashval = NACKTAB_HASHVAL(*who, notice->z_uid);
975     for (nacked = nacktab[hashval]; nacked; nacked = nacked->next) {
976         if (nacked->dest.addr.sin_addr.s_addr == who->sin_addr.s_addr
977             && nacked->dest.addr.sin_port == who->sin_port
978             && ZCompareUID(&nacked->uid, &notice->z_uid)) {
979             if (nacked->client)
980                 nacked->client->last_ack = NOW;
981             timer_reset(nacked->timer);
982             free(nacked->packet);
983             LIST_DELETE(nacked);
984             free(nacked);
985             return;
986         }
987     }
988
989 #if 1
990     zdbug((LOG_DEBUG,"nack_cancel: nack not found %s:%08X,%08X",
991            inet_ntoa (notice->z_uid.zuid_addr),
992            notice->z_uid.tv.tv_sec, notice->z_uid.tv.tv_usec));
993 #endif
994 }
995
996 /* for compatibility when sending subscription information to old clients */
997 #ifdef OLD_COMPAT
998 #define OLD_ZEPHYR_VERSION      "ZEPH0.0"
999 #endif /* OLD_COMPAT */
1000
1001 /* Dispatch an HM_CTL notice. */
1002
1003 Code_t
1004 hostm_dispatch(notice, auth, who, server)
1005     ZNotice_t *notice;
1006     int auth;
1007     struct sockaddr_in *who;
1008     Server *server;
1009 {
1010     Server *owner;
1011     char *opcode = notice->z_opcode;
1012     Code_t retval;
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(notice, auth, who, server)
1091     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_KRB4
1169         /* in case it's changed */
1170         memcpy(client->session_key, ZGetSession(), sizeof(C_Block));
1171 #endif
1172         retval = subscr_subscribe(client, notice, server);
1173         if (retval != ZERR_NONE) {
1174             syslog(LOG_WARNING, "subscr failed: %s", error_message(retval));
1175             if (server == me_server)
1176                 nack(notice, who);
1177             return ZERR_NONE;
1178         }
1179     } else if (strcmp(opcode, CLIENT_UNSUBSCRIBE) == 0) {
1180         client = client_find(&who->sin_addr, notice->z_port);
1181         if (client != NULL) {
1182             if (strcmp(client->principal->string, notice->z_sender) != 0) {
1183                 /* you may only cancel for your own clients */
1184                 if (server == me_server)
1185                     clt_ack(notice, who, AUTH_FAILED);
1186                 return ZERR_NONE;
1187             }
1188 #if 0
1189             if (zdebug) {
1190                 if (server == me_server) {
1191                     syslog(LOG_DEBUG, "subscription cancel for %s/%d\n",
1192                            inet_ntoa(who->sin_addr), ntohs(who->sin_port));
1193                 } else {
1194                     syslog(LOG_DEBUG,
1195                            "subscription cancel for %s/%d from %s\n",
1196                            inet_ntoa(who->sin_addr), ntohs(who->sin_port),
1197                            server->addr_str);
1198                 }
1199             }
1200 #endif
1201             subscr_cancel(who, notice);
1202         } else {
1203             nack(notice, who);
1204             return ZERR_NONE;
1205         }
1206     } else if (strcmp(opcode, CLIENT_CANCELSUB) == 0) {
1207         /* canceling subscriptions implies I can punt info about this client */
1208         client = client_find(&who->sin_addr, notice->z_port);
1209         if (client == NULL) {
1210 #if 0
1211             zdbug((LOG_DEBUG,"can_sub not found client"));
1212 #endif
1213             if (server == me_server)
1214                 nack(notice, who);
1215             return ZERR_NONE;
1216         }
1217         if (strcmp(client->principal->string, notice->z_sender) != 0) {
1218             /* you may only cancel for your own clients */
1219             if (server == me_server)
1220                 clt_ack(notice, who, AUTH_FAILED);
1221             return ZERR_NONE;
1222         }
1223         /* don't flush locations here, let him do it explicitly */
1224 #if 0
1225         zdbug((LOG_DEBUG, "cancelsub clt_dereg %s/%d",
1226                inet_ntoa(who->sin_addr), ntohs(who->sin_port)));
1227 #endif
1228         client_deregister(client, 0);
1229     } else {
1230         syslog(LOG_WARNING, "unknown ctl opcode %s", opcode); 
1231         if (server == me_server) {
1232             if (strcmp(notice->z_class_inst, ZEPHYR_CTL_REALM) != 0)
1233                 nack(notice, who);
1234         }
1235         return ZERR_NONE;
1236     }
1237
1238     if (server == me_server) {
1239         ack(notice, who);
1240         server_forward(notice, auth, who);
1241     }
1242     return ZERR_NONE;
1243 }
1244
1245 void
1246 hostm_shutdown()
1247 {
1248     int i, s, newserver;
1249     struct sockaddr_in sin;
1250
1251     for (i = 0; i < nservers; i++) {
1252         if (i != me_server_idx && otherservers[i].state == SERV_UP)
1253             break;
1254     }
1255     newserver = (i < nservers);
1256     for (i = 0; i < num_hosts; i++) {
1257         sin.sin_addr = hosts[i];
1258         sin.sin_port = hm_port;
1259         if (newserver) {
1260             while (1) {
1261                 s = (random() % (nservers - 1)) + 1;
1262                 if (otherservers[s].state == SERV_UP)
1263                     break;
1264             }
1265             hostm_deathgram(&sin, &otherservers[s]);
1266         } else {
1267             hostm_deathgram(&sin, NULL);
1268         }
1269     }
1270 }
1271
1272 void
1273 realm_shutdown()
1274 {
1275     int i, s, newserver;
1276     struct sockaddr_in sin;
1277
1278     for (i = 0; i < nservers; i++) {
1279         if (i != me_server_idx && otherservers[i].state == SERV_UP)
1280             break;
1281     }
1282     zdbug((LOG_DEBUG, "rlm_shutdown"));
1283
1284     newserver = (i < nservers);
1285     if (newserver) {
1286       while (1) {
1287         s = (random() % (nservers - 1)) + 1;
1288         if (otherservers[s].state == SERV_UP)
1289           break;
1290       }
1291       realm_deathgram(&otherservers[s]);
1292     } else {
1293       realm_deathgram(NULL);
1294     }
1295 }
1296
1297 static void
1298 hostm_deathgram(sin, server)
1299     struct sockaddr_in *sin;
1300     Server *server;
1301 {
1302     Code_t retval;
1303     int shutlen;
1304     ZNotice_t shutnotice;
1305     char *shutpack;
1306
1307     memset (&shutnotice, 0, sizeof(shutnotice));
1308
1309     shutnotice.z_kind = HMCTL;
1310     shutnotice.z_port = sin->sin_port; /* we are sending it */
1311     shutnotice.z_class = HM_CTL_CLASS;
1312     shutnotice.z_class_inst = HM_CTL_SERVER;
1313     shutnotice.z_opcode = SERVER_SHUTDOWN;
1314     shutnotice.z_sender = HM_CTL_SERVER;
1315     shutnotice.z_recipient = hm_recipient();
1316     shutnotice.z_default_format = "";
1317     shutnotice.z_num_other_fields = 0;
1318     shutnotice.z_message = (server) ? server->addr_str : NULL;
1319     shutnotice.z_message_len = (server) ? strlen(server->addr_str) + 1 : 0;
1320
1321     retval = ZFormatNotice(&shutnotice, &shutpack, &shutlen, ZNOAUTH);
1322     if (retval != ZERR_NONE) {
1323         syslog(LOG_ERR, "hm_shut format: %s",error_message(retval));
1324         return;
1325     }
1326     retval = ZSetDestAddr(sin);
1327     if (retval != ZERR_NONE) {
1328         syslog(LOG_WARNING, "hm_shut set addr: %s", error_message(retval));
1329         free(shutpack);
1330         return;
1331     }
1332     retval = ZSendPacket(shutpack, shutlen, 0);
1333     if (retval != ZERR_NONE)
1334         syslog(LOG_WARNING, "hm_shut xmit: %s", error_message(retval));
1335     free(shutpack);
1336 }
1337
1338 static char *
1339 hm_recipient()
1340 {
1341     static char *recipient;
1342     char *realm;
1343
1344     if (recipient)
1345         return recipient;
1346
1347     realm = ZGetRealm();
1348     if (!realm)
1349         realm = "???";
1350     recipient = (char *) malloc(strlen(realm) + 4);
1351     strcpy (recipient, "hm@");
1352     strcat (recipient, realm);
1353     return recipient;
1354 }
1355