1 /* This file is part of the Project Athena Zephyr Notification System.
2 * It contains functions for dispatching a notice.
4 * Created by: John T. Kohl
6 * $Source: /afs/dev.mit.edu/source/repository/athena/lib/zephyr/server/dispatch.c,v $
7 * $Author: kcr@ATHENA.MIT.EDU $
9 * Copyright (c) 1987, 1991 by the Massachusetts Institute of Technology.
10 * For copying and distribution information, see the file
14 #include <zephyr/mit-copyright.h>
16 #include <sys/socket.h>
20 static const char rcsid_dispatch_c[] =
21 "$Id: dispatch.c 2527 2009-08-09 18:42:32Z kcr@ATHENA.MIT.EDU $";
26 NACKTAB_HASHSIZE = 1023
29 nacktab_hashval(struct sockaddr_in sa, ZUnique_Id_t uid) {
30 return (sa.sin_addr.s_addr ^
32 uid.zuid_addr.s_addr ^
34 uid.tv.tv_usec) % NACKTAB_HASHSIZE;
37 #define HOSTS_SIZE_INIT 256
40 const char *ZNoticeKinds[9] = {"UNSAFE", "UNACKED", "ACKED", "HMACK",
41 "HMCTL", "SERVACK", "SERVNAK", "CLIENTACK",
48 * void dispatch(notice, auth, who)
51 * struct sockaddr_in *who;
53 * void clt_ack(notice, who, sent)
55 * struct sockaddr_in *who;
58 * void nack_release(client)
61 * void sendit(notice, auth, who, external)
64 * struct sockaddr_in *who;
67 * void xmit(notice, dest, auth, client)
69 * struct sockaddr_in *dest;
75 String *class_control, *class_admin, *class_hm, *class_ulogin, *class_ulocate;
77 int rexmit_times[] = REXMIT_TIMES;
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);
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"};
98 static Unacked *nacktab[NACKTAB_HASHSIZE];
99 static struct in_addr *hosts;
100 static int hosts_size = 0, num_hosts = 0;
104 dump_stats (void *arg)
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);
122 /* log stuff once an hour */
123 timer_set_rel ((long) 6*60*60, dump_stats, arg);
128 * Handle an input packet.
129 * Warning: this function may be called from within a brain dump.
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 */
147 static int first_time = 1;
151 /* Dump statistics five minutes after startup */
154 timer_set_rel(5*60, dump_stats, NULL);
159 if (otherservers[me_server_idx].queue) {
160 /* something here for me; take care of it */
161 zdbug((LOG_DEBUG, "internal queue process"));
163 pending = server_dequeue(me_server);
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));
170 dispatch(&new_notice, pending->auth, &pending->who, 1);
172 server_pending_free(pending);
177 * nothing in internal queue, go to the external library
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));
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));
194 if (server_which_server(&whoisit)) {
195 /* we need to parse twice--once to get
196 the source addr, second to check
198 notice_extract_address(&new_notice, &input_sin);
199 /* Should check to see if packet is from another realm's server,
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;
213 realm = realm_which_realm(whence);
214 authentic = ZCheckSrvAuthentication(&new_notice, whence, realm ? realm->name : NULL);
217 message_notices.val++;
218 dispatch(&new_notice, authentic, &whoisit, from_server);
226 dispatch(ZNotice_t *notice,
228 struct sockaddr_in *who,
232 String *notice_class;
233 struct sockaddr_in who2;
238 char dbg_buf[BUFSIZ];
241 authflag = (auth == ZAUTH_YES);
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));
250 if (notice->z_kind == CLIENTACK) {
251 nack_cancel(notice, who);
257 notice_class = make_string(notice->z_class,1);
260 interserver_notices.val++;
261 status = server_dispatch(notice, authflag, who);
262 } else if (class_is_hm(notice_class)) {
264 status = hostm_dispatch(notice, authflag, who, me_server);
265 } else if (realm_which_realm(who) && !(class_is_admin(notice_class))) {
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)) {
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)) {
279 status = server_adispatch(notice, authflag, who, me_server);
281 if (!realm_bound_for_realm(ZGetRealm(), notice->z_recipient)) {
282 cp = strchr(notice->z_recipient, '@');
284 !(realm = realm_get_realm_by_name(cp + 1))) {
285 /* Foreign user, local realm */
286 sendit(notice, authflag, who, 0);
288 realm_handoff(notice, authflag, who, realm, 1);
290 if (notice->z_recipient[0] == '@')
291 notice->z_recipient = "";
292 sendit(notice, authflag, who, 1);
294 free_string(notice_class);
298 if (status == ZSRV_REQUEUE)
299 server_self_queue(notice, authflag, who);
300 free_string(notice_class);
304 * Send a notice off to those clients who have subscribed to it.
308 sendit(ZNotice_t *notice,
310 struct sockaddr_in *who,
313 static int send_counter = 0;
314 char recipbuf[MAX_PRINCIPAL_SIZE], *recipp;
320 class = make_string(notice->z_class, 1);
321 if (realm_bound_for_realm(ZGetRealm(), notice->z_recipient)) {
324 acl = class_get_acl(class);
326 /* if controlled and not auth, fail */
328 syslog(LOG_WARNING, "sendit unauthentic %s from %s",
329 notice->z_class, notice->z_sender);
330 clt_ack(notice, who, AUTH_FAILED);
334 /* if from foreign realm server, disallow if not realm of sender */
335 rlm = realm_which_realm(who);
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);
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);
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);
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
370 if (send_counter == 0)
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);
380 strncpy(recipbuf, notice->z_recipient, sizeof(recipbuf));
381 recipp = strrchr(recipbuf, '@');
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.
387 strncpy(recipp + 1, realm_expand_realm(recipp + 1),
388 sizeof(recipbuf) - (recipp - recipbuf) - 1);
389 dest.recip = make_string(recipbuf, 0);
392 if (send_to_dest(notice, auth, &dest, send_counter, external))
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))
403 free_string(dest.recip);
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
417 send_to_dest(ZNotice_t *notice,
426 clientp = triplet_lookup(dest);
430 for (; *clientp; clientp++) {
431 if ((*clientp)->last_send == send_counter)
433 (*clientp)->last_send = send_counter;
434 if ((*clientp)->realm) {
436 realm_handoff(notice, auth, &clientp[0]->addr, clientp[0]->realm,
441 xmit(notice, &((*clientp)->addr), auth, *clientp);
450 * Release anything destined for the client in the not-yet-acked table.
454 nack_release(Client *client)
457 Unacked *nacked, *next;
459 for (i = 0; i < NACKTAB_HASHSIZE; i++) {
460 for (nacked = nacktab[i]; nacked; nacked = next) {
462 if (nacked->client == client) {
463 timer_reset(nacked->timer);
464 Unacked_delete(nacked);
465 free(nacked->packet);
473 * Send one packet of a fragmented message to a client. After transmitting,
474 * put it onto the not ack'ed list.
477 /* the arguments must be the same as the arguments to Z_XmitFragment */
480 xmit_frag(ZNotice_t *notice,
485 struct sockaddr_in sin;
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)
499 /* now we've sent it, mark it as not ack'ed */
500 nacked = (Unacked *) malloc(sizeof(Unacked));
502 /* no space: just punt */
503 syslog(LOG_WARNING, "xmit_frag nack malloc");
507 savebuf = (char *) malloc(len);
509 /* no space: just punt */
510 syslog(LOG_WARNING, "xmit_frag pack malloc");
515 memcpy(savebuf, buf, len);
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);
531 * Send the notice to the client. After transmitting, put it onto the
536 xmit(ZNotice_t *notice,
537 struct sockaddr_in *dest,
543 int packlen, sendfail = 0;
546 noticepack = (char *) malloc(sizeof(ZPacket_t));
548 syslog(LOG_ERR, "xmit malloc");
549 return; /* DON'T put on nack list */
552 packlen = sizeof(ZPacket_t);
554 if (auth && client) { /*
555 we are distributing authentic and
556 we have a pointer to auth info
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));
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));
575 #else /* !HAVE_KRB4 */
577 retval = ZFormatSmallRawNotice(notice, noticepack, &packlen);
578 if (retval != ZERR_NONE) {
579 syslog(LOG_ERR, "xmit auth/raw format: %s", error_message(retval));
583 #endif /* HAVE_KRB4 */
584 #endif /* HAVE_KRB5 */
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.
595 if (retval == ZERR_PKTLEN) {
596 ZNotice_t partnotice;
599 int buffer_len, hdrlen, offset, fragsize, message_len;
600 int origoffset, origlen;
604 retval = ZSetDestAddr(dest);
605 if (retval != ZERR_NONE) {
606 syslog(LOG_WARNING, "xmit set addr: %s", error_message(retval));
610 partnotice = *notice;
612 partnotice.z_auth = 0;
613 partnotice.z_authent_len = 0;
614 partnotice.z_ascii_authent = (char *)"";
616 origoffset = offset = fragsize = 0;
617 origlen = notice->z_message_len;
619 buffer = (char *) malloc(sizeof(ZPacket_t));
621 syslog(LOG_ERR, "xmit unauth refrag malloc");
622 return; /* DON'T put on nack list */
624 buffer_len = sizeof(ZPacket_t);
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");
634 if (notice->z_multinotice && strcmp(notice->z_multinotice, ""))
635 if (sscanf(notice->z_multinotice, "%d/%d", &origoffset, &origlen)
638 syslog(LOG_WARNING, "xmit unauth refrag: parse failed");
643 fragsize = Z_MAXPKTLEN-hdrlen-Z_FRAGFUDGE;
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;
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,
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));
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;
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));
675 (void) memcpy(ptr, partnotice.z_message, partnotice.z_message_len);
677 buffer_len = hdrlen+partnotice.z_message_len;
679 xmit_frag(&partnotice, buffer, buffer_len, 0);
683 if (!notice->z_message_len)
689 /* End of refrag code */
691 if (retval != ZERR_NONE) {
692 syslog(LOG_ERR, "xmit format: %s", error_message(retval));
694 return; /* DON'T put on nack list */
697 retval = ZSetDestAddr(dest);
698 if (retval != ZERR_NONE) {
699 syslog(LOG_WARNING, "xmit set addr: %s", error_message(retval));
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) {
714 /* now we've sent it, mark it as not ack'ed */
716 nacked = (Unacked *) malloc(sizeof(Unacked));
718 /* no space: just punt */
719 syslog(LOG_WARNING, "xmit nack malloc");
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);
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.
743 Unacked *nacked = (Unacked *) arg;
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);
751 if (rexmit_times[nacked->rexmits] == -1) {
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.)
760 Unacked_delete(nacked);
761 if (nacked->client) {
762 server_kill_clt(nacked->client);
763 client_deregister(nacked->client, 1);
765 free(nacked->packet);
769 /* The client has sent us an ack recently. Retry with the maximum
770 * retransmit time. */
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));
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)
787 /* reset the timer */
788 nacked->timer = timer_set_rel(rexmit_times[nacked->rexmits], rexmit,
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.
801 clt_ack(ZNotice_t *notice,
802 struct sockaddr_in *who,
811 if (bdumping) { /* don't ack while dumping */
812 zdbug((LOG_DEBUG,"bdumping, no ack"));
818 acknotice.z_kind = SERVACK;
821 acknotice.z_message = ZSRVACK_SENT;
825 acknotice.z_message = ZSRVACK_FAIL;
826 acknotice.z_kind = SERVNAK;
830 acknotice.z_kind = SERVNAK;
831 acknotice.z_message = ZSRVACK_NOTSENT;
832 sent_name = "nak/not_sent";
835 acknotice.z_message = ZSRVACK_NOTSENT;
836 sent_name = "not_sent";
842 acknotice.z_multinotice = "";
844 /* leave room for the trailing null */
845 acknotice.z_message_len = strlen(acknotice.z_message) + 1;
847 packlen = sizeof(ackpack);
849 retval = ZFormatSmallRawNotice(&acknotice, ackpack, &packlen);
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 = "";
859 retval = ZFormatSmallRawNotice(&acknotice, ackpack, &packlen);
862 if (retval != ZERR_NONE) {
863 syslog(LOG_ERR, "clt_ack format: %s", error_message(retval));
866 retval = ZSetDestAddr(who);
867 if (retval != ZERR_NONE) {
868 syslog(LOG_WARNING, "clt_ack set addr: %s", error_message(retval));
871 retval = ZSendPacket(ackpack, packlen, 0);
872 if (retval != ZERR_NONE) {
873 syslog(LOG_WARNING, "clt_ack xmit: %s", error_message(retval));
876 zdbug((LOG_DEBUG, "packet sent"));
882 * An ack has arrived.
883 * remove the packet matching this notice from the not-yet-acked queue
887 nack_cancel(ZNotice_t *notice,
888 struct sockaddr_in *who)
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, ¬ice->z_uid)) {
900 nacked->client->last_ack = NOW;
901 timer_reset(nacked->timer);
902 free(nacked->packet);
903 Unacked_delete(nacked);
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));
914 /* for compatibility when sending subscription information to old clients */
916 #define OLD_ZEPHYR_VERSION "ZEPH0.0"
917 #endif /* OLD_COMPAT */
919 /* Dispatch an HM_CTL notice. */
922 hostm_dispatch(ZNotice_t *notice,
924 struct sockaddr_in *who,
927 char *opcode = notice->z_opcode;
928 int i, add = 0, remove = 0;
930 if (notice->z_kind == HMACK) {
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);
946 } else if (strcmp(opcode, HM_ATTACH) == 0) {
947 if (server == me_server) {
948 server_forward(notice, auth, who);
954 } else if (strcmp(opcode, HM_DETACH) == 0) {
957 syslog(LOG_WARNING, "hm_dispatch: unknown opcode %s", opcode);
961 for (i = 0; i < num_hosts; i++) {
962 if (hosts[i].s_addr == who->sin_addr.s_addr)
965 if (i == num_hosts) {
966 if (hosts_size == 0) {
967 hosts = (struct in_addr *) malloc(HOSTS_SIZE_INIT *
968 sizeof(struct in_addr));
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));
979 hosts[num_hosts++] = who->sin_addr;
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));
994 * Dispatch a ZEPHYR_CTL notice.
998 control_dispatch(ZNotice_t *notice,
1000 struct sockaddr_in *who,
1003 char *opcode = notice->z_opcode;
1008 struct sockaddr_in newwho;
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: ""
1018 zdbug((LOG_DEBUG, "ctl_disp: opc=%s", opcode));
1020 notice_extract_address(notice, &newwho);
1021 realm = realm_which_realm(&newwho);
1023 return(realm_control_dispatch(notice, auth, who, server, realm));
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 */
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)
1038 #else /* !OLD_COMPAT */
1040 #endif /* OLD_COMPAT */
1041 subscr_sendlist(notice, auth, who);
1044 if (server == me_server)
1045 clt_ack(notice, who, AUTH_FAILED);
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);
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);
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);
1077 retval = krb5_copy_keyblock(Z_krb5_ctx, ZGetSession(),
1078 &client->session_keyblock);
1081 syslog(LOG_WARNING, "keyblock copy failed in subscr: %s",
1082 error_message(retval));
1083 if (server == me_server)
1089 /* in case it's changed */
1090 memcpy(client->session_key, ZGetSession(), sizeof(C_Block));
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)
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);
1109 subscr_cancel(who, notice);
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)
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);
1128 /* don't flush locations here, let him do it explicitly */
1129 client_deregister(client, 0);
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)
1139 if (server == me_server) {
1141 server_forward(notice, auth, who);
1147 hostm_shutdown(void)
1149 int i, s, newserver;
1150 struct sockaddr_in sin;
1152 for (i = 0; i < nservers; i++) {
1153 if (i != me_server_idx && otherservers[i].state == SERV_UP)
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;
1163 s = (random() % (nservers - 1)) + 1;
1164 if (otherservers[s].state == SERV_UP)
1167 hostm_deathgram(&sin, &otherservers[s]);
1169 hostm_deathgram(&sin, NULL);
1175 realm_shutdown(void)
1177 int i, s, newserver;
1179 for (i = 0; i < nservers; i++) {
1180 if (i != me_server_idx && otherservers[i].state == SERV_UP)
1183 zdbug((LOG_DEBUG, "rlm_shutdown"));
1185 newserver = (i < nservers);
1188 s = (random() % (nservers - 1)) + 1;
1189 if (otherservers[s].state == SERV_UP)
1192 realm_deathgram(&otherservers[s]);
1194 realm_deathgram(NULL);
1199 hostm_deathgram(struct sockaddr_in *sin,
1204 ZNotice_t shutnotice;
1207 memset (&shutnotice, 0, sizeof(shutnotice));
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;
1221 retval = ZFormatNotice(&shutnotice, &shutpack, &shutlen, ZNOAUTH);
1222 if (retval != ZERR_NONE) {
1223 syslog(LOG_ERR, "hm_shut format: %s",error_message(retval));
1226 retval = ZSetDestAddr(sin);
1227 if (retval != ZERR_NONE) {
1228 syslog(LOG_WARNING, "hm_shut set addr: %s", error_message(retval));
1232 retval = ZSendPacket(shutpack, shutlen, 0);
1233 if (retval != ZERR_NONE)
1234 syslog(LOG_WARNING, "hm_shut xmit: %s", error_message(retval));
1241 static char *recipient;
1247 realm = (char *)ZGetRealm();
1250 recipient = (char *) malloc(strlen(realm) + 4);
1251 strcpy (recipient, "hm@");
1252 strcat (recipient, realm);