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