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 * $Id: dispatch.c,v 1.60 2000/02/11 20:59:36 ghudson Exp $
8 * Copyright (c) 1987, 1991 by the Massachusetts Institute of Technology.
9 * For copying and distribution information, see the file
13 #include <zephyr/mit-copyright.h>
15 #include <sys/socket.h>
19 static const char rcsid_dispatch_c[] =
20 "$Id: dispatch.c,v 1.60 2000/02/11 20:59:36 ghudson Exp $";
24 #define NACKTAB_HASHSIZE 1023
25 #define NACKTAB_HASHVAL(sockaddr, uid) (((sockaddr).sin_addr.s_addr ^ \
26 (sockaddr).sin_port ^ \
27 (uid).zuid_addr.s_addr ^ \
29 (uid).tv.tv_usec) % NACKTAB_HASHSIZE)
30 #define HOSTS_SIZE_INIT 256
33 ZCONST char *ZNoticeKinds[9] = {"UNSAFE", "UNACKED", "ACKED", "HMACK",
34 "HMCTL", "SERVACK", "SERVNAK", "CLIENTACK",
41 * void dispatch(notice, auth, who)
44 * struct sockaddr_in *who;
46 * void clt_ack(notice, who, sent)
48 * struct sockaddr_in *who;
51 * void nack_release(client)
54 * void sendit(notice, auth, who)
57 * struct sockaddr_in *who;
59 * void xmit(notice, dest, auth, client)
61 * struct sockaddr_in *dest;
67 String *class_control, *class_admin, *class_hm, *class_ulogin, *class_ulocate;
69 int rexmit_times[] = REXMIT_TIMES;
71 static void nack_cancel __P((ZNotice_t *, struct sockaddr_in *));
72 static void dispatch __P((ZNotice_t *, int, struct sockaddr_in *, int));
73 static int send_to_dest __P((ZNotice_t *, int, Destination *dest, int, int));
74 static void hostm_deathgram __P((struct sockaddr_in *, Server *));
75 static char *hm_recipient __P((void));
77 Statistic realm_notices = {0, "inter-realm notices"};
78 Statistic interserver_notices = {0, "inter-server notices"};
79 Statistic hm_packets = {0, "hostmanager packets"};
80 Statistic control_notices = {0, "client control notices"};
81 Statistic message_notices = {0, "message notices"};
82 Statistic login_notices = {0, "login notices"};
83 Statistic i_s_ctls = {0, "inter-server control notices"};
84 Statistic i_s_logins = {0, "inter-server login notices"};
85 Statistic i_s_admins = {0, "inter-server admin notices"};
86 Statistic i_s_locates = {0, "inter-server locate notices"};
87 Statistic locate_notices = {0, "locate notices"};
88 Statistic admin_notices = {0, "admin notices"};
90 static Unacked *nacktab[NACKTAB_HASHSIZE];
91 static struct in_addr *hosts;
92 static int hosts_size = 0, num_hosts = 0;
98 syslog(LOG_INFO, "stats: %s: %d", hm_packets.str, hm_packets.val);
99 syslog(LOG_INFO, "stats: %s: %d", control_notices.str,
100 control_notices.val);
101 syslog(LOG_INFO, "stats: %s: %d", message_notices.str,
102 message_notices.val);
103 syslog(LOG_INFO, "stats: %s: %d", login_notices.str, login_notices.val);
104 syslog(LOG_INFO, "stats: %s: %d", locate_notices.str, locate_notices.val);
105 syslog(LOG_INFO, "stats: %s: %d", admin_notices.str, admin_notices.val);
106 syslog(LOG_INFO, "stats: %s: %d", realm_notices.str, realm_notices.val);
107 syslog(LOG_INFO, "stats: %s: %d", interserver_notices.str,
108 interserver_notices.val);
109 syslog(LOG_INFO, "stats: %s: %d", i_s_ctls.str, i_s_ctls.val);
110 syslog(LOG_INFO, "stats: %s: %d", i_s_logins.str, i_s_logins.val);
111 syslog(LOG_INFO, "stats: %s: %d", i_s_admins.str, i_s_admins.val);
112 syslog(LOG_INFO, "stats: %s: %d", i_s_locates.str, i_s_locates.val);
114 /* log stuff once an hour */
115 timer_set_rel ((long) 6*60*60, dump_stats, arg);
119 * Handle an input packet.
120 * Warning: this function may be called from within a brain dump.
127 ZPacket_t input_packet; /* from the network */
128 ZNotice_t new_notice; /* parsed from input_packet */
129 int input_len; /* len of packet */
130 struct sockaddr_in input_sin; /* Zconstructed for authent */
131 struct sockaddr_in whoisit; /* for holding peer's address */
132 int authentic; /* authentic flag */
133 Pending *pending; /* pending packet */
134 int from_server; /* packet is from another server */
135 Realm *realm; /* foreign realm ptr */
137 static int first_time = 1;
141 /* Dump statistics five minutes after startup */
144 timer_set_rel(5*60, dump_stats, NULL);
149 if (otherservers[me_server_idx].queue) {
150 /* something here for me; take care of it */
152 zdbug((LOG_DEBUG, "internal queue process"));
155 pending = server_dequeue(me_server);
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));
162 dispatch(&new_notice, pending->auth, &pending->who, 1);
164 server_pending_free(pending);
169 * nothing in internal queue, go to the external library
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));
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));
185 if (server_which_server(&whoisit)) {
186 /* we need to parse twice--once to get
187 the source addr, second to check
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 realm = realm_which_realm(&input_sin);
195 authentic = ZCheckRealmAuthentication(&new_notice, &input_sin,
198 authentic = ZCheckAuthentication(&new_notice, &input_sin);
203 realm = realm_which_realm(&whoisit);
205 authentic = ZCheckRealmAuthentication(&new_notice, &whoisit,
208 authentic = ZCheckAuthentication(&new_notice, &whoisit);
212 if (whoisit.sin_port != hm_port && whoisit.sin_port != hm_srv_port &&
213 strcasecmp(new_notice.z_class, ZEPHYR_ADMIN_CLASS) != 0 &&
214 whoisit.sin_port != srv_addr.sin_port &&
215 new_notice.z_kind != CLIENTACK) {
216 syslog(LOG_ERR, "bad port %s/%d", inet_ntoa(whoisit.sin_addr),
217 ntohs(whoisit.sin_port));
221 message_notices.val++;
222 dispatch(&new_notice, authentic, &whoisit, from_server);
230 dispatch(notice, auth, who, from_server)
233 struct sockaddr_in *who;
237 String *notice_class;
238 struct sockaddr_in who2;
243 char dbg_buf[BUFSIZ];
246 authflag = (auth == ZAUTH_YES);
248 if ((int) notice->z_kind < (int) UNSAFE ||
249 (int) notice->z_kind > (int) CLIENTACK) {
250 syslog(LOG_NOTICE, "bad notice kind 0x%x from %s", notice->z_kind,
251 inet_ntoa(who->sin_addr));
257 "disp:%s '%s' '%s' '%s' notice to '%s' from '%s' %s/%d/%d",
258 ZNoticeKinds[(int) notice->z_kind], notice->z_class,
259 notice->z_class_inst, notice->z_opcode, notice->z_recipient,
260 notice->z_sender, inet_ntoa(who->sin_addr),
261 ntohs(who->sin_port), ntohs(notice->z_port));
262 syslog(LOG_DEBUG, "%s", dbg_buf);
266 if (notice->z_kind == CLIENTACK) {
267 nack_cancel(notice, who);
273 if (0 && from_server) {
274 /* incorporate server_dispatch here */
277 notice_class = make_string(notice->z_class,1);
280 interserver_notices.val++;
281 status = server_dispatch(notice, authflag, who);
282 } else if (class_is_hm(notice_class)) {
284 status = hostm_dispatch(notice, authflag, who, me_server);
285 } else if (realm_which_realm(who) && !(class_is_admin(notice_class))) {
287 status = realm_dispatch(notice, authflag, who, me_server);
288 } else if (class_is_control(notice_class)) {
289 control_notices.val++;
290 status = control_dispatch(notice, authflag, who, me_server);
291 } else if (class_is_ulogin(notice_class)) {
293 status = ulogin_dispatch(notice, authflag, who, me_server);
294 } else if (class_is_ulocate(notice_class)) {
295 locate_notices.val++;
296 status = ulocate_dispatch(notice, authflag, who, me_server);
297 } else if (class_is_admin(notice_class)) {
299 status = server_adispatch(notice, authflag, who, me_server);
301 if (!bound_for_local_realm(notice)) {
302 cp = strchr(notice->z_recipient, '@');
304 !(realm = realm_get_realm_by_name(realm_expand_realm(cp + 1))))
305 sendit(notice, authflag, who, 0);
307 realm_handoff(notice, authflag, who, realm, 1);
309 if (notice->z_recipient[0] == '@')
310 notice->z_recipient = "";
311 sendit(notice, authflag, who, 1);
313 free_string(notice_class);
317 if (status == ZSRV_REQUEUE)
318 server_self_queue(notice, authflag, who);
319 free_string(notice_class);
323 * Send a notice off to those clients who have subscribed to it.
327 sendit(notice, auth, who, external)
330 struct sockaddr_in *who;
333 static int send_counter = 0;
334 char recipbuf[ANAME_SZ + INST_SZ + REALM_SZ + 3], *recipp;
340 class = make_string(notice->z_class, 1);
341 acl = class_get_acl(class);
343 /* if controlled and not auth, fail */
345 syslog(LOG_WARNING, "sendit unauthentic %s from %s",
346 notice->z_class, notice->z_sender);
347 clt_ack(notice, who, AUTH_FAILED);
351 /* if not auth to transmit, fail */
352 if (!access_check(notice->z_sender, acl, TRANSMIT)) {
353 syslog(LOG_WARNING, "sendit unauthorized %s from %s",
354 notice->z_class, notice->z_sender);
355 clt_ack(notice, who, AUTH_FAILED);
359 /* sender != inst and not auth to send to others --> fail */
360 if (strcmp(notice->z_sender, notice->z_class_inst) != 0 &&
361 !access_check(notice->z_sender, acl, INSTUID)) {
362 syslog(LOG_WARNING, "sendit unauth uid %s %s.%s", notice->z_sender,
363 notice->z_class, notice->z_class_inst);
364 clt_ack(notice, who, AUTH_FAILED);
369 if (!realm_which_realm(who)) {
370 if (memcmp(¬ice->z_sender_addr.s_addr, &who->sin_addr.s_addr,
371 sizeof(notice->z_sender_addr.s_addr))) {
372 /* someone is playing games... */
373 /* inet_ntoa returns pointer to static area */
374 /* max size is 255.255.255.255 */
376 strcpy(buffer, inet_ntoa(who->sin_addr));
379 "sendit unauthentic fake packet: claimed %s, real %s",
380 inet_ntoa(notice->z_sender_addr), buffer);
381 clt_ack(notice, who, AUTH_FAILED);
385 if (ntohl(notice->z_sender_addr.s_addr) != 0) {
387 "sendit invalid address: claimed %s, real %s",
388 inet_ntoa(notice->z_sender_addr), buffer);
389 clt_ack(notice, who, AUTH_FAILED);
393 syslog(LOG_WARNING, "sendit addr mismatch: claimed %s, real %s",
394 inet_ntoa(notice->z_sender_addr), buffer);
398 /* Increment the send counter, used to prevent duplicate sends to
399 * clients. On the off-chance that we wrap around to 0, skip over
400 * it to prevent missing clients which have never had a packet
403 if (send_counter == 0)
406 /* Send to clients subscribed to the triplet itself. */
407 dest.classname = class;
408 dest.inst = make_string(notice->z_class_inst, 1);
409 if (bound_for_local_realm(notice) && *notice->z_recipient == '@') {
410 dest.recip = make_string("", 0);
412 strncpy(recipbuf, notice->z_recipient, sizeof(recipbuf));
413 recipp = strrchr(recipbuf, '@');
415 sprintf(recipp + 1, "%s", realm_expand_realm(recipp + 1));
416 dest.recip = make_string(recipbuf, 0);
418 if (send_to_dest(notice, auth, &dest, send_counter, external))
421 /* Send to clients subscribed to the triplet with the instance
422 * substituted with the wildcard instance. */
423 free_string(dest.inst);
424 dest.inst = wildcard_instance;
425 if (send_to_dest(notice, auth, &dest, send_counter, external))
429 free_string(dest.recip);
437 * Send to each client in the list. Avoid duplicates by setting
438 * last_send on each client to send_counter, a nonce which is updated
443 send_to_dest(notice, auth, dest, send_counter, external)
453 clientp = triplet_lookup(dest);
457 for (; *clientp; clientp++) {
458 if ((*clientp)->last_send == send_counter)
460 (*clientp)->last_send = send_counter;
461 if ((*clientp)->realm && external)
462 realm_handoff(notice, auth, &clientp[0]->addr, clientp[0]->realm,
465 xmit(notice, &((*clientp)->addr), auth, *clientp);
473 * Release anything destined for the client in the not-yet-acked table.
481 Unacked *nacked, *next;
483 for (i = 0; i < NACKTAB_HASHSIZE; i++) {
484 for (nacked = nacktab[i]; nacked; nacked = next) {
486 if (nacked->client == client) {
487 timer_reset(nacked->timer);
489 free(nacked->packet);
497 * Send one packet of a fragmented message to a client. After transmitting,
498 * put it onto the not ack'ed list.
501 /* the arguments must be the same as the arguments to Z_XmitFragment */
504 xmit_frag(notice, buf, len, waitforack)
510 struct sockaddr_in sin;
514 int hashval, sendfail = 0;
516 retval = ZSendPacket(buf, len, 0);
517 if (retval != ZERR_NONE) {
518 syslog(LOG_WARNING, "xmit_frag send: %s", error_message(retval));
519 if (retval != EAGAIN && retval != ENOBUFS)
524 /* now we've sent it, mark it as not ack'ed */
525 nacked = (Unacked *) malloc(sizeof(Unacked));
527 /* no space: just punt */
528 syslog(LOG_WARNING, "xmit_frag nack malloc");
532 savebuf = (char *) malloc(len);
534 /* no space: just punt */
535 syslog(LOG_WARNING, "xmit_frag pack malloc");
540 memcpy(savebuf, buf, len);
542 sin = ZGetDestAddr();
543 nacked->client = NULL;
544 nacked->rexmits = (sendfail) ? -1 : 0;
545 nacked->packet = savebuf;
546 nacked->dest.addr = sin;
547 nacked->packsz = len;
548 nacked->uid = notice->z_uid;
549 nacked->timer = timer_set_rel(rexmit_times[0], rexmit, nacked);
550 LIST_INSERT(&nacktab[NACKTAB_HASHVAL(sin, nacked->uid)], nacked);
555 * Send the notice to the client. After transmitting, put it onto the
560 xmit(notice, dest, auth, client)
562 struct sockaddr_in *dest;
568 int packlen, sendfail = 0;
572 zdbug((LOG_DEBUG,"xmit"));
575 noticepack = (char *) malloc(sizeof(ZPacket_t));
577 syslog(LOG_ERR, "xmit malloc");
578 return; /* DON'T put on nack list */
581 packlen = sizeof(ZPacket_t);
583 if (auth && client) { /*
584 we are distributing authentic and
585 we have a pointer to auth info
588 retval = ZFormatAuthenticNotice(notice, noticepack, packlen, &packlen,
589 client->session_key);
590 if (retval != ZERR_NONE) {
591 syslog(LOG_ERR, "xmit auth format: %s", error_message(retval));
595 #else /* !HAVE_KRB4 */
597 retval = ZFormatSmallRawNotice(notice, noticepack, &packlen);
598 if (retval != ZERR_NONE) {
599 syslog(LOG_ERR, "xmit auth/raw format: %s", error_message(retval));
603 #endif /* HAVE_KRB4 */
606 notice->z_authent_len = 0;
607 notice->z_ascii_authent = (char *)"";
608 retval = ZFormatSmallRawNotice(notice, noticepack, &packlen);
609 if (retval != ZERR_NONE) {
610 syslog(LOG_ERR, "xmit format: %s", error_message(retval));
612 return; /* DON'T put on nack list */
616 zdbug((LOG_DEBUG," to %s/%d", inet_ntoa(dest->sin_addr),
617 ntohs(dest->sin_port)));
619 retval = ZSetDestAddr(dest);
620 if (retval != ZERR_NONE) {
621 syslog(LOG_WARNING, "xmit set addr: %s", error_message(retval));
625 retval = ZSendPacket(noticepack, packlen, 0);
626 if (retval != ZERR_NONE) {
627 syslog(LOG_WARNING, "xmit xmit: (%s/%d) %s", inet_ntoa(dest->sin_addr),
628 ntohs(dest->sin_port), error_message(retval));
629 if (retval != EAGAIN && retval != ENOBUFS) {
636 /* now we've sent it, mark it as not ack'ed */
638 nacked = (Unacked *) malloc(sizeof(Unacked));
640 /* no space: just punt */
641 syslog(LOG_WARNING, "xmit nack malloc");
646 nacked->client = client;
647 nacked->rexmits = (sendfail) ? -1 : 0;
648 nacked->packet = noticepack;
649 nacked->dest.addr = *dest;
650 nacked->packsz = packlen;
651 nacked->uid = notice->z_uid;
652 nacked->timer = timer_set_rel(rexmit_times[0], rexmit, nacked);
653 LIST_INSERT(&nacktab[NACKTAB_HASHVAL(*dest, nacked->uid)], nacked);
657 * Retransmit the packet specified. If we have timed out or retransmitted
658 * too many times, punt the packet and initiate the host recovery algorithm
659 * Else, increment the count and re-send the notice packet.
666 Unacked *nacked = (Unacked *) arg;
670 syslog(LOG_DEBUG, "rexmit %s/%d #%d time %d",
671 inet_ntoa(nacked->dest.addr.sin_addr),
672 ntohs(nacked->dest.addr.sin_port), nacked->rexmits + 1, NOW);
676 if (rexmit_times[nacked->rexmits] == -1) {
678 || NOW - nacked->client->last_ack >= CLIENT_GIVEUP_MIN) {
679 /* The client (if there was one) has been unresponsive.
680 * Give up sending this packet, and kill the client if
681 * there was one. (Make sure to remove nacked from the
682 * nack list before calling client_deregister(), which
683 * scans the nack list.)
686 if (nacked->client) {
687 server_kill_clt(nacked->client);
688 client_deregister(nacked->client, 1);
690 free(nacked->packet);
694 /* The client has sent us an ack recently. Retry with the maximum
695 * retransmit time. */
700 /* retransmit the packet */
702 zdbug((LOG_DEBUG," to %s/%d", inet_ntoa(nacked->dest.addr.sin_addr),
703 ntohs(nacked->dest.addr.sin_port)));
705 retval = ZSetDestAddr(&nacked->dest.addr);
706 if (retval != ZERR_NONE) {
707 syslog(LOG_WARNING, "rexmit set addr: %s", error_message(retval));
709 retval = ZSendPacket(nacked->packet, nacked->packsz, 0);
710 if (retval != ZERR_NONE)
711 syslog(LOG_WARNING, "rexmit xmit: %s", error_message(retval));
712 if (retval == EAGAIN || retval == ENOBUFS)
716 /* reset the timer */
717 nacked->timer = timer_set_rel(rexmit_times[nacked->rexmits], rexmit,
723 * Send an acknowledgement to the sending client, by sending back the
724 * header from the original notice with the z_kind field changed to either
725 * SERVACK or SERVNAK, and the contents of the message either SENT or
726 * NOT_SENT, depending on the value of the sent argument.
730 clt_ack(notice, who, sent)
732 struct sockaddr_in *who;
742 if (bdumping) { /* don't ack while dumping */
744 zdbug((LOG_DEBUG,"bdumping, no ack"));
751 acknotice.z_kind = SERVACK;
754 acknotice.z_message = ZSRVACK_SENT;
758 acknotice.z_message = ZSRVACK_FAIL;
759 acknotice.z_kind = SERVNAK;
763 acknotice.z_kind = SERVNAK;
764 acknotice.z_message = ZSRVACK_NOTSENT;
765 sent_name = "nak/not_sent";
768 acknotice.z_message = ZSRVACK_NOTSENT;
769 sent_name = "not_sent";
776 zdbug((LOG_DEBUG,"clt_ack type %s for %d to %s/%d", sent_name,
777 ntohs(notice->z_port), inet_ntoa(who->sin_addr),
778 ntohs(who->sin_port)));
781 acknotice.z_multinotice = "";
783 /* leave room for the trailing null */
784 acknotice.z_message_len = strlen(acknotice.z_message) + 1;
786 packlen = sizeof(ackpack);
788 retval = ZFormatSmallRawNotice(&acknotice, ackpack, &packlen);
789 if (retval != ZERR_NONE) {
790 syslog(LOG_ERR, "clt_ack format: %s", error_message(retval));
793 retval = ZSetDestAddr(who);
794 if (retval != ZERR_NONE) {
795 syslog(LOG_WARNING, "clt_ack set addr: %s", error_message(retval));
798 retval = ZSendPacket(ackpack, packlen, 0);
799 if (retval != ZERR_NONE) {
800 syslog(LOG_WARNING, "clt_ack xmit: %s", error_message(retval));
803 zdbug((LOG_DEBUG, "packet sent"));
809 * An ack has arrived.
810 * remove the packet matching this notice from the not-yet-acked queue
814 nack_cancel(notice, who)
816 struct sockaddr_in *who;
821 /* search the not-yet-acked table for this packet, and flush it. */
823 zdbug((LOG_DEBUG, "nack_cancel: %s:%08X,%08X",
824 inet_ntoa(notice->z_uid.zuid_addr),
825 notice->z_uid.tv.tv_sec, notice->z_uid.tv.tv_usec));
827 hashval = NACKTAB_HASHVAL(*who, notice->z_uid);
828 for (nacked = nacktab[hashval]; nacked; nacked = nacked->next) {
829 if (nacked->dest.addr.sin_addr.s_addr == who->sin_addr.s_addr
830 && nacked->dest.addr.sin_port == who->sin_port
831 && ZCompareUID(&nacked->uid, ¬ice->z_uid)) {
833 nacked->client->last_ack = NOW;
834 timer_reset(nacked->timer);
835 free(nacked->packet);
843 zdbug((LOG_DEBUG,"nack_cancel: nack not found %s:%08X,%08X",
844 inet_ntoa (notice->z_uid.zuid_addr),
845 notice->z_uid.tv.tv_sec, notice->z_uid.tv.tv_usec));
849 /* for compatibility when sending subscription information to old clients */
851 #define OLD_ZEPHYR_VERSION "ZEPH0.0"
852 #endif /* OLD_COMPAT */
854 /* Dispatch an HM_CTL notice. */
857 hostm_dispatch(notice, auth, who, server)
860 struct sockaddr_in *who;
864 char *opcode = notice->z_opcode;
866 int i, add = 0, remove = 0;
869 zdbug((LOG_DEBUG,"hm_disp"));
872 if (notice->z_kind == HMACK) {
875 } else if (notice->z_kind != HMCTL) {
877 zdbug((LOG_DEBUG, "bogus HM packet"));
879 clt_ack(notice, who, AUTH_FAILED);
880 } else if (strcmp(opcode, HM_FLUSH) == 0) {
881 client_flush_host(&who->sin_addr);
882 if (server == me_server)
883 server_forward(notice, auth, who);
884 } else if (strcmp(opcode, HM_BOOT) == 0) {
885 client_flush_host(&who->sin_addr);
886 if (server == me_server) {
887 server_forward(notice, auth, who);
891 } else if (strcmp(opcode, HM_ATTACH) == 0) {
892 if (server == me_server) {
893 server_forward(notice, auth, who);
899 } else if (strcmp(opcode, HM_DETACH) == 0) {
902 syslog(LOG_WARNING, "hm_dispatch: unknown opcode %s", opcode);
906 for (i = 0; i < num_hosts; i++) {
907 if (hosts[i].s_addr == who->sin_addr.s_addr)
910 if (i == num_hosts) {
911 if (hosts_size == 0) {
912 hosts = (struct in_addr *) malloc(HOSTS_SIZE_INIT *
913 sizeof(struct in_addr));
916 hosts_size = HOSTS_SIZE_INIT;
917 } else if (num_hosts == hosts_size) {
918 hosts = (struct in_addr *) realloc(hosts, hosts_size * 2 *
919 sizeof(struct in_addr));
924 hosts[num_hosts++] = who->sin_addr;
927 for (i = 0; i < num_hosts; i++) {
928 if (hosts[i].s_addr == who->sin_addr.s_addr) {
929 memmove(&hosts[i], &hosts[i + 1], num_hosts - (i + 1));
939 * Dispatch a ZEPHYR_CTL notice.
943 control_dispatch(notice, auth, who, server)
946 struct sockaddr_in *who;
949 char *opcode = notice->z_opcode;
954 struct sockaddr_in newwho;
957 * ZEPHYR_CTL Opcodes expected are:
958 * BOOT (inst HM): host has booted; flush data.
959 * CLIENT_SUBSCRIBE: process with the subscription mananger.
960 * CLIENT_UNSUBSCRIBE: ""
961 * CLIENT_CANCELSUB: ""
964 zdbug((LOG_DEBUG, "ctl_disp: opc=%s", opcode));
966 newwho.sin_addr.s_addr = notice->z_sender_addr.s_addr;
967 newwho.sin_port = notice->z_port;
968 realm = realm_which_realm(&newwho);
970 return(realm_control_dispatch(notice, auth, who, server, realm));
972 if (strcasecmp(notice->z_class_inst, ZEPHYR_CTL_HM) == 0) {
973 return hostm_dispatch(notice, auth, who, server);
974 } else if (strcmp(opcode, CLIENT_GIMMESUBS) == 0 ||
975 strcmp(opcode, CLIENT_GIMMEDEFS) == 0) {
976 /* this special case is before the auth check so that
977 someone who has no subscriptions does NOT get a SERVNAK
978 but rather an empty list. Note we must therefore
979 check authentication inside subscr_sendlist */
981 /* only acknowledge if *not* old version; the old version
982 acknowledges the packet with the reply */
983 if (strcmp(notice->z_version, OLD_ZEPHYR_VERSION) != 0)
985 #else /* !OLD_COMPAT */
987 #endif /* OLD_COMPAT */
988 subscr_sendlist(notice, auth, who);
992 zdbug((LOG_DEBUG,"unauth ctrl_disp"));
994 if (server == me_server)
995 clt_ack(notice, who, AUTH_FAILED);
999 wantdefs = strcmp(opcode, CLIENT_SUBSCRIBE_NODEFS);
1000 if (!wantdefs || strcmp(opcode, CLIENT_SUBSCRIBE) == 0) {
1001 /* subscription notice */
1002 retval = client_register(notice, &who->sin_addr, &client, wantdefs);
1003 if (retval != ZERR_NONE) {
1004 syslog(LOG_NOTICE, "subscr %s/%s/%d failed: %s",
1005 notice->z_sender, inet_ntoa(who->sin_addr),
1006 ntohs(notice->z_port), error_message(retval));
1007 if (server == me_server) {
1008 if (retval == ZSRV_BADSUBPORT)
1009 clt_ack(notice, who, AUTH_FAILED);
1015 if (strcmp(client->principal->string, notice->z_sender) != 0) {
1016 /* you may only subscribe for your own clients */
1017 if (server == me_server)
1018 clt_ack(notice, who, AUTH_FAILED);
1022 /* in case it's changed */
1023 memcpy(client->session_key, ZGetSession(), sizeof(C_Block));
1025 retval = subscr_subscribe(client, notice);
1026 if (retval != ZERR_NONE) {
1027 syslog(LOG_WARNING, "subscr failed: %s", error_message(retval));
1028 if (server == me_server)
1032 } else if (strcmp(opcode, CLIENT_UNSUBSCRIBE) == 0) {
1033 client = client_find(&who->sin_addr, notice->z_port);
1034 if (client != NULL) {
1035 if (strcmp(client->principal->string, notice->z_sender) != 0) {
1036 /* you may only cancel for your own clients */
1037 if (server == me_server)
1038 clt_ack(notice, who, AUTH_FAILED);
1043 if (server == me_server) {
1044 syslog(LOG_DEBUG, "subscription cancel for %s/%d\n",
1045 inet_ntoa(who->sin_addr), ntohs(who->sin_port));
1048 "subscription cancel for %s/%d from %s\n",
1049 inet_ntoa(who->sin_addr), ntohs(who->sin_port),
1054 subscr_cancel(who, notice);
1059 } else if (strcmp(opcode, CLIENT_CANCELSUB) == 0) {
1060 /* canceling subscriptions implies I can punt info about this client */
1061 client = client_find(&who->sin_addr, notice->z_port);
1062 if (client == NULL) {
1064 zdbug((LOG_DEBUG,"can_sub not found client"));
1066 if (server == me_server)
1070 if (strcmp(client->principal->string, notice->z_sender) != 0) {
1071 /* you may only cancel for your own clients */
1072 if (server == me_server)
1073 clt_ack(notice, who, AUTH_FAILED);
1076 /* don't flush locations here, let him do it explicitly */
1078 zdbug((LOG_DEBUG, "cancelsub clt_dereg %s/%d",
1079 inet_ntoa(who->sin_addr), ntohs(who->sin_port)));
1081 client_deregister(client, 0);
1083 syslog(LOG_WARNING, "unknown ctl opcode %s", opcode);
1084 if (server == me_server)
1089 if (server == me_server) {
1091 server_forward(notice, auth, who);
1099 int i, s, newserver;
1100 struct sockaddr_in sin;
1102 for (i = 0; i < nservers; i++) {
1103 if (i != me_server_idx && otherservers[i].state == SERV_UP)
1106 newserver = (i < nservers);
1107 for (i = 0; i < num_hosts; i++) {
1108 sin.sin_addr = hosts[i];
1109 sin.sin_port = hm_port;
1112 s = (random() % (nservers - 1)) + 1;
1113 if (otherservers[s].state == SERV_UP)
1116 hostm_deathgram(&sin, &otherservers[s]);
1118 hostm_deathgram(&sin, NULL);
1124 hostm_deathgram(sin, server)
1125 struct sockaddr_in *sin;
1130 ZNotice_t shutnotice;
1133 shutnotice.z_kind = HMCTL;
1134 shutnotice.z_port = sin->sin_port; /* we are sending it */
1135 shutnotice.z_class = HM_CTL_CLASS;
1136 shutnotice.z_class_inst = HM_CTL_SERVER;
1137 shutnotice.z_opcode = SERVER_SHUTDOWN;
1138 shutnotice.z_sender = HM_CTL_SERVER;
1139 shutnotice.z_recipient = hm_recipient();
1140 shutnotice.z_default_format = "";
1141 shutnotice.z_num_other_fields = 0;
1142 shutnotice.z_message = (server) ? server->addr_str : NULL;
1143 shutnotice.z_message_len = (server) ? strlen(server->addr_str) + 1 : 0;
1145 retval = ZFormatNotice(&shutnotice, &shutpack, &shutlen, ZNOAUTH);
1146 if (retval != ZERR_NONE) {
1147 syslog(LOG_ERR, "hm_shut format: %s",error_message(retval));
1150 retval = ZSetDestAddr(sin);
1151 if (retval != ZERR_NONE) {
1152 syslog(LOG_WARNING, "hm_shut set addr: %s", error_message(retval));
1156 retval = ZSendPacket(shutpack, shutlen, 0);
1157 if (retval != ZERR_NONE)
1158 syslog(LOG_WARNING, "hm_shut xmit: %s", error_message(retval));
1165 static char *recipient;
1171 realm = ZGetRealm();
1174 recipient = (char *) malloc(strlen(realm) + 4);
1175 strcpy (recipient, "hm@");
1176 strcat (recipient, realm);