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