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