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