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