summaryrefslogtreecommitdiff
path: root/server/dispatch.c.auth
diff options
context:
space:
mode:
authorGravatar Craig Fields <cfields@mit.edu>1994-11-13 02:28:50 +0000
committerGravatar Craig Fields <cfields@mit.edu>1994-11-13 02:28:50 +0000
commitb829891a761b11c6106bb277ec04af79850c8c50 (patch)
treeaa7c590006f94fed924bdc0970b2b3057c8f4324 /server/dispatch.c.auth
parent07c6c399486e93589a1cfcffcc972c5fae93c35a (diff)
Initial revision
Diffstat (limited to 'server/dispatch.c.auth')
-rw-r--r--server/dispatch.c.auth1078
1 files changed, 1078 insertions, 0 deletions
diff --git a/server/dispatch.c.auth b/server/dispatch.c.auth
new file mode 100644
index 0000000..8707c2f
--- /dev/null
+++ b/server/dispatch.c.auth
@@ -0,0 +1,1078 @@
+/* This file is part of the Project Athena Zephyr Notification System.
+ * It contains functions for dispatching a notice.
+ *
+ * Created by: John T. Kohl
+ *
+ * $Source$
+ * $Author$
+ *
+ * Copyright (c) 1987, 1991 by the Massachusetts Institute of Technology.
+ * For copying and distribution information, see the file
+ * "mit-copyright.h".
+ */
+
+#include <zephyr/mit-copyright.h>
+
+#ifndef lint
+#ifndef SABER
+static char rcsid_dispatch_c[] =
+ "$Id$";
+#endif
+#endif
+
+#include "zserver.h"
+#include <sys/socket.h>
+
+#ifdef DEBUG
+Zconst char *ZNoticeKinds[9] = {"UNSAFE", "UNACKED", "ACKED", "HMACK",
+ "HMCTL", "SERVACK", "SERVNAK", "CLIENTACK",
+ "STAT"};
+#endif
+/*
+ *
+ * External Routines:
+ *
+ * void dispatch(notice, auth, who)
+ * ZNotice_t *notice;
+ * int auth;
+ * struct sockaddr_in *who;
+ *
+ * void clt_ack(notice, who, sent)
+ * ZNotice_t *notice;
+ * struct sockaddr_in *who;
+ * ZSentType sent;
+ *
+ * void nack_release(client)
+ * ZClient_t *client;
+ *
+ * void sendit(notice, auth, who)
+ * ZNotice_t *notice;
+ * int auth;
+ * struct sockaddr_in *who;
+ *
+ * void xmit(notice, dest, auth, client)
+ * ZNotice_t *notice;
+ * struct sockaddr_in *dest;
+ * int auth;
+ * ZClient_t *client;
+ */
+
+
+/* patchable magic numbers controlling the retransmission rate and count */
+int num_rexmits = NUM_REXMITS;
+long rexmit_secs = REXMIT_SECS;
+long abs_timo = REXMIT_SECS*NUM_REXMITS + 10;
+
+ZSTRING *class_control, *class_admin, *class_hm, *class_ulogin,
+ *class_ulocate;
+
+#ifdef __STDC__
+# define P(s) s
+#else
+# define P(s) ()
+#endif
+
+static void nack_cancel P((register ZNotice_t *, struct sockaddr_in *));
+static void dispatch P((ZNotice_t *, int, struct sockaddr_in *, int));
+static int send_to_dest P((ZNotice_t *, int, ZDestination *dest, int));
+
+#undef P
+
+ZStatistic interserver_notices = {0, "inter-server notices"};
+ZStatistic hm_packets = {0, "hostmanager packets"};
+ZStatistic control_notices = {0, "client control notices"};
+ZStatistic message_notices = {0, "message notices"};
+ZStatistic login_notices = {0, "login notices"};
+ZStatistic i_s_ctls = {0, "inter-server control notices"};
+ZStatistic i_s_logins = {0, "inter-server login notices"};
+ZStatistic i_s_admins = {0, "inter-server admin notices"};
+ZStatistic i_s_locates = {0, "inter-server locate notices"};
+ZStatistic locate_notices = {0, "locate notices"};
+ZStatistic admin_notices = {0, "admin notices"};
+
+static void
+#ifdef __STDC__
+dump_stats (void *arg)
+#else
+dump_stats (arg)
+void *arg;
+#endif
+{
+ syslog(LOG_INFO, "stats: %s: %d", hm_packets.str, hm_packets.val);
+ syslog(LOG_INFO, "stats: %s: %d", control_notices.str,
+ control_notices.val);
+ syslog(LOG_INFO, "stats: %s: %d", message_notices.str,
+ message_notices.val);
+ syslog(LOG_INFO, "stats: %s: %d", login_notices.str,
+ login_notices.val);
+ syslog(LOG_INFO, "stats: %s: %d", locate_notices.str,
+ locate_notices.val);
+ syslog(LOG_INFO, "stats: %s: %d", admin_notices.str,
+ admin_notices.val);
+ syslog(LOG_INFO, "stats: %s: %d", interserver_notices.str,
+ interserver_notices.val);
+ syslog(LOG_INFO, "stats: %s: %d", i_s_ctls.str, i_s_ctls.val);
+ syslog(LOG_INFO, "stats: %s: %d", i_s_logins.str, i_s_logins.val);
+ syslog(LOG_INFO, "stats: %s: %d", i_s_admins.str, i_s_admins.val);
+ syslog(LOG_INFO, "stats: %s: %d", i_s_locates.str, i_s_locates.val);
+ /* log stuff once an hour */
+ (void) timer_set_rel ((long) 6*60*60, dump_stats, arg);
+}
+
+/*
+ * Handle an input packet.
+ * Warning: this function may be called from within a brain dump.
+ */
+
+void
+handle_packet()
+{
+ Code_t status;
+ ZPacket_t input_packet; /* from the network */
+ ZNotice_t new_notice; /* parsed from input_packet */
+ int input_len; /* len of packet */
+ struct sockaddr_in input_sin; /* Zconstructed for authent */
+ struct sockaddr_in whoisit; /* for holding peer's address */
+ int authentic; /* authentic flag */
+ ZSrvPending_t *pending; /* pending packet */
+ ZHostList_t *host; /* host ptr */
+ int from_server; /* packet is from another server */
+#ifdef DEBUG
+ static int first_time = 1;
+#endif
+
+#ifdef DEBUG
+ /* Dump statistics five minutes after startup */
+ if (first_time) {
+ first_time = 0;
+ (void) timer_set_rel (5*60, dump_stats, (void *) 0);
+ }
+#endif
+ /* handle traffic */
+
+ if (otherservers[me_server_idx].zs_update_queue) {
+ /* something here for me; take care of it */
+#if 1
+ zdbug((LOG_DEBUG, "internal queue process"));
+#endif
+
+ pending = otherservers[me_server_idx].zs_update_queue->q_forw;
+ host = hostm_find_host(&(pending->pend_who.sin_addr));
+ if (host && host->zh_locked) {
+ /* can't deal with it now. to preserve ordering,
+ we can't process other packets, esp. since we
+ may block since we don't really know if there
+ are things in the real queue. */
+#if 1
+ zdbug((LOG_DEBUG,"host %s is locked",
+ inet_ntoa(host->zh_addr.sin_addr)));
+#endif
+ return;
+ }
+ pending = server_dequeue(me_server); /* we can do it, remove */
+
+ if ((status = ZParseNotice(pending->pend_packet,
+ pending->pend_len,
+ &new_notice)) != ZERR_NONE) {
+ syslog(LOG_ERR,
+ "bad notice parse (%s): %s",
+ inet_ntoa(pending->pend_who.sin_addr),
+ error_message(status));
+ } else
+ dispatch(&new_notice, pending->pend_auth,
+ &pending->pend_who, 1);
+ server_pending_free(pending);
+ return;
+ }
+ /*
+ * nothing in internal queue, go to the external library
+ * queue/socket
+ */
+ if ((status = ZReceivePacket(input_packet,
+ &input_len,
+ &whoisit)) != ZERR_NONE) {
+ syslog(LOG_ERR,
+ "bad packet receive: %s from %s",
+ error_message(status), inet_ntoa(whoisit.sin_addr));
+ return;
+ }
+ npackets++;
+ if ((status = ZParseNotice(input_packet,
+ input_len,
+ &new_notice)) != ZERR_NONE) {
+ syslog(LOG_ERR,
+ "bad notice parse (%s): %s",
+ inet_ntoa(whoisit.sin_addr),
+ error_message(status));
+ return;
+ }
+ if (server_which_server(&whoisit)) {
+ /* we need to parse twice--once to get
+ the source addr, second to check
+ authentication */
+ (void) memset((caddr_t) &input_sin, 0,
+ sizeof(input_sin));
+ input_sin.sin_addr.s_addr = new_notice.z_sender_addr.s_addr;
+ input_sin.sin_port = new_notice.z_port;
+ input_sin.sin_family = AF_INET;
+ authentic = ZCheckAuthentication(&new_notice, &input_sin);
+ from_server = 1;
+ } else {
+ from_server = 0;
+ authentic = ZCheckAuthentication(&new_notice, &whoisit);
+ }
+
+ if (whoisit.sin_port != hm_port &&
+ strcasecmp (new_notice.z_class,ZEPHYR_ADMIN_CLASS) &&
+ whoisit.sin_port != sock_sin.sin_port &&
+ new_notice.z_kind != CLIENTACK) {
+ syslog(LOG_ERR,
+ "bad port %s/%d",
+ inet_ntoa(whoisit.sin_addr),
+ ntohs(whoisit.sin_port));
+ return;
+ }
+
+ message_notices.val++;
+ dispatch(&new_notice, authentic, &whoisit, from_server);
+ return;
+}
+/*
+ * Dispatch a notice.
+ */
+
+static void
+dispatch(notice, auth, who, from_server)
+ ZNotice_t *notice;
+ int auth;
+ struct sockaddr_in *who;
+ int from_server;
+{
+ Code_t status;
+ ZSTRING *notice_class;
+ struct sockaddr_in who2;
+ int authflag;
+#ifdef DEBUG
+ char dbg_buf[BUFSIZ];
+#endif
+
+ /* Set "authflag" to 1 or 0 for handler functions. Treat
+ * ZAUTH_CKSUM_FAILED as authentic except for sendit(), which is
+ * handled below. */
+ switch (auth) {
+ case ZAUTH_YES:
+ case ZAUTH_CKSUM_FAILED:
+ authflag = 1;
+ break;
+ case ZAUTH_FAILED:
+ case ZAUTH_NO:
+ default:
+ authflag = 0;
+ break;
+ }
+
+ if ((int) notice->z_kind < (int) UNSAFE ||
+ (int) notice->z_kind > (int) CLIENTACK) {
+ syslog(LOG_NOTICE, "bad notice kind 0x%x from %s",
+ (int) notice->z_kind,
+ inet_ntoa(who->sin_addr));
+ return;
+ }
+#if 0
+ if (zdebug) {
+ (void) sprintf (dbg_buf,
+ "disp:%s '%s' '%s' '%s' notice to '%s' from '%s' %s/%d/%d",
+ ZNoticeKinds[(int) notice->z_kind],
+ notice->z_class,
+ notice->z_class_inst,
+ notice->z_opcode,
+ notice->z_recipient,
+ notice->z_sender,
+ inet_ntoa(who->sin_addr),
+ ntohs(who->sin_port),
+ ntohs(notice->z_port));
+ syslog (LOG_DEBUG, "%s", dbg_buf);
+ }
+#endif
+
+ if (notice->z_kind == CLIENTACK) {
+ nack_cancel(notice, who);
+ return;
+ }
+
+ who2 = *who;
+#if 0
+ if (0 && from_server) {
+ /* incorporate server_dispatch here */
+ }
+#endif
+ notice_class = make_zstring(notice->z_class,1);
+
+ if (from_server) {
+ interserver_notices.val++;
+ status = server_dispatch(notice, auth, who);
+ } else if (class_is_hm(notice_class)) {
+ hm_packets.val++;
+ status = hostm_dispatch(notice, auth, who, me_server);
+ } else if (class_is_control(notice_class)) {
+ control_notices.val++;
+ status = control_dispatch(notice, auth, who, me_server);
+ } else if (class_is_ulogin(notice_class)) {
+ login_notices.val++;
+ status = ulogin_dispatch(notice, auth, who, me_server);
+ } else if (class_is_ulocate(notice_class)) {
+ locate_notices.val++;
+ status = ulocate_dispatch(notice, auth, who, me_server);
+ } else if (class_is_admin(notice_class)) {
+ admin_notices.val++;
+ status = server_adispatch(notice, auth, who, me_server);
+ } else {
+ if (auth == ZAUTH_CKSUM_FAILED)
+ authflag = 0;
+ sendit (notice, auth, who);
+ free_zstring(notice_class);
+ return;
+ }
+
+ if (status == ZSRV_REQUEUE) {
+#ifdef CONCURRENT
+ server_self_queue(notice, auth, who);
+#else
+ syslog(LOG_ERR, "requeue while not concurr");
+ abort();
+#endif
+ }
+ free_zstring(notice_class);
+}
+
+/*
+ * Send a notice off to those clients who have subscribed to it.
+ */
+
+void
+sendit(notice, auth, who)
+ ZNotice_t *notice;
+ int auth;
+ struct sockaddr_in *who;
+{
+ static int send_counter = 0;
+ int any = 0;
+ ZAcl_t *acl;
+ ZDestination dest;
+ ZSTRING *class;
+
+ class = make_zstring(notice->z_class,1);
+ if ((acl = class_get_acl(class)) != NULLZACLT) {
+ /* if controlled and not auth, fail */
+ if (!auth) {
+ syslog(LOG_WARNING, "sendit unauthentic %s from %s",
+ notice->z_class, notice->z_sender);
+ clt_ack(notice, who, AUTH_FAILED);
+ free_zstring(class);
+ return;
+ }
+ /* if not auth to transmit, fail */
+ if (!access_check(notice->z_sender, acl, TRANSMIT)) {
+ syslog(LOG_WARNING, "sendit unauthorized %s from %s",
+ notice->z_class, notice->z_sender);
+ clt_ack(notice, who, AUTH_FAILED);
+ free_zstring(class);
+ return;
+ }
+ /* sender != inst and not auth to send to others --> fail */
+ if ((strcmp (notice->z_sender, notice->z_class_inst)) &&
+ (!access_check(notice->z_sender, acl, INSTUID))) {
+ syslog(LOG_WARNING,
+ "sendit unauth uid %s %s.%s",
+ notice->z_sender,
+ notice->z_class,
+ notice->z_class_inst);
+ clt_ack(notice, who, AUTH_FAILED);
+ free_zstring(class);
+ return;
+ }
+ }
+ if (memcmp(&notice->z_sender_addr.s_addr, &who->sin_addr.s_addr,
+ sizeof(notice->z_sender_addr.s_addr))) {
+ /* someone is playing games... */
+ /* inet_ntoa returns pointer to static area */
+ /* max size is 255.255.255.255 */
+ char buffer[16];
+ (void) strcpy(buffer, inet_ntoa(who->sin_addr));
+ if (!auth) {
+ syslog(LOG_WARNING, "sendit unauthentic fake packet: claimed %s, real %s",
+ inet_ntoa(notice->z_sender_addr), buffer);
+ clt_ack(notice, who, AUTH_FAILED);
+ free_zstring(class);
+ return;
+ }
+ if (ntohl(notice->z_sender_addr.s_addr) != 0) {
+ syslog(LOG_WARNING, "sendit invalid address: claimed %s, real %s",
+ inet_ntoa(notice->z_sender_addr), buffer);
+ clt_ack(notice, who, AUTH_FAILED);
+ free_zstring(class);
+ return;
+ }
+ syslog(LOG_WARNING, "sendit addr mismatch: claimed %s, real %s",
+ inet_ntoa(notice->z_sender_addr), buffer);
+ }
+
+ /* Increment the send counter, used to prevent duplicate sends to
+ * clients. On the off-chance that we wrap around to 0, skip over
+ * it to prevent missing clients which have never had a packet
+ * sent to them. */
+ send_counter++;
+ if (send_counter == 0)
+ send_counter = 1;
+
+ /* Send to clients subscribed to the triplet itself. */
+ dest.classname = class;
+ dest.inst = make_zstring(notice->z_class_inst, 1);
+ dest.recip = make_zstring(notice->z_recipient, 0);
+ if (send_to_dest(notice, auth, &dest, send_counter))
+ any = 1;
+
+ /* Send to clients subscribed to the triplet with the instance
+ * substituted with the wildcard instance. */
+ free_zstring(dest.inst);
+ dest.inst = wildcard_instance;
+ if (send_to_dest(notice, auth, &dest, send_counter))
+ any = 1;
+
+ free_zstring(class);
+ free_zstring(dest.recip);
+ if (any)
+ ack(notice, who);
+ else
+ nack(notice, who);
+}
+
+/*
+ * Send to each client in the list. Avoid duplicates by setting
+ * last_send on each client to send_counter, a nonce which is updated
+ * by sendit() above.
+ */
+
+static int
+send_to_dest(notice, auth, dest, send_counter)
+ ZNotice_t *notice;
+ int auth;
+ ZDestination *dest;
+ int send_counter;
+{
+ register ZClientList_t *list, *p;
+ register ZClient_t *client;
+ register int any = 0;
+
+ list = triplet_lookup(dest);
+ if (list != NULLZCLT) {
+ for (p = list->q_forw; p != list; p = p->q_forw) {
+ client = p->zclt_client;
+ if (client->last_send == send_counter)
+ continue;
+ client->last_send = send_counter;
+ xmit(notice, &(client->zct_sin), auth, client);
+ any = 1;
+ }
+ }
+ return any;
+}
+
+/*
+ * Clean up the not-yet-acked queue and release anything destined
+ * for the client.
+ */
+
+void
+nack_release(client)
+ ZClient_t *client;
+{
+ register ZNotAcked_t *nacked, *nack2;
+
+ /* search the not-yet-acked list for anything destined to him, and
+ flush it. */
+ for (nacked = nacklist->q_forw;
+ nacked != nacklist;)
+ if ((nacked->na_addr.sin_addr.s_addr == client->zct_sin.sin_addr.s_addr) &&
+ (nacked->na_addr.sin_port == client->zct_sin.sin_port)) {
+ /* go back, since remque will change things */
+ nack2 = nacked->q_back;
+ timer_reset(nacked->na_timer);
+ xremque(nacked);
+ xfree(nacked->na_packet);
+ xfree(nacked);
+ /* now that the remque adjusted the linked list,
+ we go forward again */
+ nacked = nack2->q_forw;
+ } else
+ nacked = nacked->q_forw;
+ return;
+}
+
+/*
+ * Send one packet of a fragmented message to a client. After transmitting,
+ * put it onto the not ack'ed list.
+ */
+
+/* the arguments must be the same as the arguments to Z_XmitFragment */
+/*ARGSUSED*/
+Code_t
+xmit_frag(notice, buf, len, waitforack)
+ ZNotice_t *notice;
+ char *buf;
+ int len;
+ int waitforack;
+{
+ char *savebuf;
+ register ZNotAcked_t *nacked;
+ Code_t retval;
+
+ if ((retval = ZSendPacket(buf, len, 0)) != ZERR_NONE) {
+ syslog(LOG_WARNING, "xmit_frag send: %s",
+ error_message(retval));
+ return(retval);
+ }
+
+ /* now we've sent it, mark it as not ack'ed */
+
+ if (!(nacked = (ZNotAcked_t *)xmalloc(sizeof(ZNotAcked_t)))) {
+ /* no space: just punt */
+ syslog(LOG_WARNING, "xmit_frag nack malloc");
+ return(ENOMEM);
+ }
+
+ if (!(savebuf = (char *)xmalloc(len))) {
+ /* no space: just punt */
+ syslog(LOG_WARNING, "xmit_frag pack malloc");
+ return(ENOMEM);
+ }
+
+ (void) memcpy(savebuf, buf, len);
+
+ nacked->na_rexmits = 0;
+ nacked->na_packet = savebuf;
+ nacked->na_srv_idx = 0;
+ nacked->na_addr = ZGetDestAddr();
+ nacked->na_packsz = len;
+ nacked->na_uid = notice->z_uid;
+ nacked->q_forw = nacked->q_back = nacked;
+ nacked->na_abstimo = NOW + abs_timo;
+
+ /* set a timer to retransmit when done */
+ nacked->na_timer = timer_set_rel(rexmit_secs,
+ rexmit,
+ (void *) nacked);
+ /* chain in */
+ xinsque(nacked, nacklist);
+ return(ZERR_NONE);
+}
+
+/*
+ * Send the notice to the client. After transmitting, put it onto the
+ * not ack'ed list.
+ */
+
+void
+xmit(notice, dest, auth, client)
+ ZNotice_t *notice;
+ struct sockaddr_in *dest;
+ int auth;
+ ZClient_t *client;
+{
+ caddr_t noticepack;
+ register ZNotAcked_t *nacked;
+ int packlen;
+ Code_t retval;
+
+#if 0
+ zdbug((LOG_DEBUG,"xmit"));
+#endif
+
+ if (!(noticepack = (caddr_t) xmalloc(sizeof(ZPacket_t)))) {
+ syslog(LOG_ERR, "xmit malloc");
+ return; /* DON'T put on nack list */
+ }
+
+ packlen = sizeof(ZPacket_t);
+
+ if (auth && client) { /*
+ we are distributing authentic and
+ we have a pointer to auth info
+ */
+#ifdef KERBEROS
+
+ if ((retval = ZFormatAuthenticNotice(notice,
+ noticepack,
+ packlen,
+ &packlen,
+ client->zct_cblock))
+ != ZERR_NONE) {
+ syslog(LOG_ERR, "xmit auth format: %s",
+ error_message(retval));
+ xfree(noticepack);
+ return;
+ }
+#else /* !KERBEROS */
+ notice->z_auth = 1;
+ if ((retval = ZFormatSmallRawNotice(notice,
+ noticepack,
+ &packlen))
+ != ZERR_NONE) {
+ syslog(LOG_ERR, "xmit auth/raw format: %s",
+ error_message(retval));
+ xfree(noticepack);
+ return;
+ }
+#endif /* KERBEROS */
+ } else {
+ notice->z_auth = 0;
+ notice->z_authent_len = 0;
+ notice->z_ascii_authent = (char *)"";
+ if ((retval = ZFormatSmallRawNotice(notice,
+ noticepack,
+ &packlen)) != ZERR_NONE) {
+ syslog(LOG_ERR, "xmit format: %s",
+ error_message(retval));
+ xfree(noticepack);
+ return; /* DON'T put on nack list */
+ }
+ }
+#if 0
+ zdbug((LOG_DEBUG," to %s/%d",inet_ntoa(dest->sin_addr),
+ ntohs(dest->sin_port)));
+#endif
+ if ((retval = ZSetDestAddr(dest)) != ZERR_NONE) {
+ syslog(LOG_WARNING, "xmit set addr: %s",
+ error_message(retval));
+ xfree(noticepack);
+ return;
+ }
+ if ((retval = ZSendPacket(noticepack, packlen, 0)) != ZERR_NONE) {
+ syslog(LOG_WARNING, "xmit xmit: (%s/%d) %s",
+ inet_ntoa(dest->sin_addr), ntohs(dest->sin_port),
+ error_message(retval));
+ xfree(noticepack);
+ return;
+ }
+
+ /* now we've sent it, mark it as not ack'ed */
+
+ if (!(nacked = (ZNotAcked_t *)xmalloc(sizeof(ZNotAcked_t)))) {
+ /* no space: just punt */
+ syslog(LOG_WARNING, "xmit nack malloc");
+ xfree(noticepack);
+ return;
+ }
+
+ nacked->na_rexmits = 0;
+ nacked->na_packet = noticepack;
+ nacked->na_srv_idx = 0; /* XXX */
+ nacked->na_addr = *dest;
+ nacked->na_packsz = packlen;
+ nacked->na_uid = notice->z_uid;
+ nacked->q_forw = nacked->q_back = nacked;
+ nacked->na_abstimo = NOW + abs_timo;
+
+ /* set a timer to retransmit when done */
+ nacked->na_timer = timer_set_rel(rexmit_secs,
+ rexmit,
+ (void *) nacked);
+ /* chain in */
+ xinsque(nacked, nacklist);
+
+}
+
+
+/*
+ * Retransmit the packet specified. If we have timed out or retransmitted
+ * too many times, punt the packet and initiate the host recovery algorithm
+ * Else, increment the count and re-send the notice packet.
+ */
+
+void
+#ifdef __STDC__
+rexmit(void *arg)
+#else
+rexmit(arg)
+ void *arg;
+#endif
+{
+ register ZNotAcked_t *nackpacket = (ZNotAcked_t*) arg;
+ int retval;
+ ZNotice_t dummy_notice;
+ register ZClient_t *client;
+
+#if 1
+ zdbug((LOG_DEBUG,"rexmit"));
+#endif
+
+ if (++(nackpacket->na_rexmits) > num_rexmits ||
+ NOW > nackpacket->na_abstimo) {
+ /* possibly dead client */
+
+ dummy_notice.z_port = nackpacket->na_addr.sin_port;
+
+ client = client_which_client(&nackpacket->na_addr,
+ &dummy_notice);
+
+ /* unlink & free packet */
+ xremque(nackpacket);
+ xfree(nackpacket->na_packet);
+ xfree(nackpacket);
+
+ /* initiate recovery */
+ if (client)
+ server_recover(client);
+ return;
+ }
+
+ /* retransmit the packet */
+
+#if 0
+ zdbug((LOG_DEBUG," to %s/%d",
+ inet_ntoa(nackpacket->na_addr.sin_addr),
+ ntohs(nackpacket->na_addr.sin_port)));
+#endif
+ if ((retval = ZSetDestAddr(&nackpacket->na_addr))
+ != ZERR_NONE) {
+ syslog(LOG_WARNING, "rexmit set addr: %s",
+ error_message(retval));
+ goto requeue;
+
+ }
+ if ((retval = ZSendPacket(nackpacket->na_packet,
+ nackpacket->na_packsz, 0)) != ZERR_NONE)
+ syslog(LOG_WARNING, "rexmit xmit: %s", error_message(retval));
+
+requeue:
+ /* reset the timer */
+ nackpacket->na_timer = timer_set_rel(rexmit_secs,
+ rexmit,
+ (void *) nackpacket);
+ return;
+
+}
+
+/*
+ * Send an acknowledgement to the sending client, by sending back the
+ * header from the original notice with the z_kind field changed to either
+ * SERVACK or SERVNAK, and the contents of the message either SENT or
+ * NOT_SENT, depending on the value of the sent argument.
+ */
+
+void
+clt_ack(notice, who, sent)
+ ZNotice_t *notice;
+ struct sockaddr_in *who;
+ ZSentType sent;
+{
+ ZNotice_t acknotice;
+ ZPacket_t ackpack;
+ int packlen;
+ int notme = 0;
+ char *sent_name;
+ Code_t retval;
+
+ if (bdumping) { /* don't ack while dumping */
+#if 1
+ zdbug((LOG_DEBUG,"bdumping, no ack"));
+#endif
+ return;
+ }
+
+ acknotice = *notice;
+
+ acknotice.z_kind = SERVACK;
+ switch (sent) {
+ case SENT:
+ acknotice.z_message = ZSRVACK_SENT;
+ sent_name = "sent";
+ break;
+ case NOT_FOUND:
+ acknotice.z_message = ZSRVACK_FAIL;
+ acknotice.z_kind = SERVNAK;
+ sent_name = "fail";
+ break;
+ case AUTH_FAILED:
+ acknotice.z_kind = SERVNAK;
+ acknotice.z_message = ZSRVACK_NOTSENT;
+ sent_name = "nak/not_sent";
+ break;
+ case NOT_SENT:
+ acknotice.z_message = ZSRVACK_NOTSENT;
+ sent_name = "not_sent";
+ break;
+ default:
+ abort ();
+ }
+
+#if 0
+ zdbug((LOG_DEBUG,"clt_ack type %s for %d to %s/%d",
+ sent_name,
+ ntohs(notice->z_port),
+ inet_ntoa(who->sin_addr),
+ ntohs(who->sin_port)));
+#endif
+
+ if (!server_which_server(who) &&
+ (hostm_find_server(&who->sin_addr) != me_server)) {
+#if 0
+ zdbug((LOG_DEBUG,"not me"));
+#endif
+ notme = 1;
+ }
+
+ acknotice.z_multinotice = "";
+
+ /* leave room for the trailing null */
+ acknotice.z_message_len = strlen(acknotice.z_message) + 1;
+
+ packlen = sizeof(ackpack);
+
+ if ((retval = ZFormatSmallRawNotice(&acknotice,
+ ackpack,
+ &packlen)) != ZERR_NONE) {
+ syslog(LOG_ERR, "clt_ack format: %s",error_message(retval));
+ return;
+ }
+ if ((retval = ZSetDestAddr(who)) != ZERR_NONE) {
+ syslog(LOG_WARNING, "clt_ack set addr: %s",
+ error_message(retval));
+ return;
+ }
+ if ((retval = ZSendPacket(ackpack, packlen, 0)) != ZERR_NONE) {
+ syslog(LOG_WARNING, "clt_ack xmit: %s", error_message(retval));
+ return;
+ }
+ else
+ zdbug ((LOG_DEBUG, "packet sent"));
+ if (notme)
+ hostm_deathgram(who, me_server);
+ return;
+}
+
+/*
+ * An ack has arrived.
+ * remove the packet matching this notice from the not-yet-acked queue
+ */
+
+static void
+nack_cancel(notice, who)
+ register ZNotice_t *notice;
+ struct sockaddr_in *who;
+{
+ register ZNotAcked_t *nacked;
+
+ /* search the not-yet-acked list for this packet, and
+ flush it. */
+#if 0
+ zdbug((LOG_DEBUG, "nack_cancel: %s:%08X,%08X",
+ inet_ntoa (notice->z_uid.zuid_addr),
+ notice->z_uid.tv.tv_sec, notice->z_uid.tv.tv_usec));
+#endif
+ for (nacked = nacklist->q_forw;
+ nacked != nacklist;
+ nacked = nacked->q_forw)
+ if ((nacked->na_addr.sin_addr.s_addr == who->sin_addr.s_addr) &&
+ (nacked->na_addr.sin_port == who->sin_port))
+ if (ZCompareUID(&nacked->na_uid, &notice->z_uid)) {
+ timer_reset(nacked->na_timer);
+ xfree(nacked->na_packet);
+ xremque(nacked);
+ xfree(nacked);
+ return;
+ }
+#if 1
+ zdbug((LOG_DEBUG,"nack_cancel: nack not found %s:%08X,%08X",
+ inet_ntoa (notice->z_uid.zuid_addr),
+ notice->z_uid.tv.tv_sec, notice->z_uid.tv.tv_usec));
+#endif
+ return;
+}
+
+/* for compatibility when sending subscription information to old clients */
+#ifdef OLD_COMPAT
+#define OLD_ZEPHYR_VERSION "ZEPH0.0"
+#endif /* OLD_COMPAT */
+
+/*
+ * Dispatch a ZEPHYR_CTL notice.
+ */
+
+Code_t
+control_dispatch(notice, auth, who, server)
+ ZNotice_t *notice;
+ int auth;
+ struct sockaddr_in *who;
+ ZServerDesc_t *server;
+{
+ register char *opcode = notice->z_opcode;
+ ZClient_t *client;
+ ZHostList_t *host;
+ Code_t retval;
+ int wantdefs;
+
+ /*
+ * ZEPHYR_CTL Opcodes expected are:
+ * BOOT (inst HM): host has booted; flush data.
+ * CLIENT_SUBSCRIBE: process with the subscription mananger.
+ * CLIENT_UNSUBSCRIBE: ""
+ * CLIENT_CANCELSUB: ""
+ */
+
+ zdbug ((LOG_DEBUG, "ctl_disp: opc=%s", opcode));
+
+ if (!strcasecmp (notice->z_class_inst, ZEPHYR_CTL_HM))
+ return(hostm_dispatch(notice, auth, who, server));
+ else if (!strcmp (opcode, CLIENT_GIMMESUBS) ||
+ !strcmp (opcode, CLIENT_GIMMEDEFS)) {
+ /* this special case is before the auth check so that
+ someone who has no subscriptions does NOT get a SERVNAK
+ but rather an empty list. Note we must therefore
+ check authentication inside subscr_sendlist */
+#ifdef OLD_COMPAT
+ /* only acknowledge if *not* old version; the old version
+ acknowledges the packet with the reply */
+ if (strcmp (notice->z_version, OLD_ZEPHYR_VERSION))
+ ack(notice, who);
+#else /* !OLD_COMPAT */
+ ack(notice, who);
+#endif /* OLD_COMPAT */
+ subscr_sendlist(notice, auth, who);
+ return(ZERR_NONE);
+ } else if (!auth) {
+#if 0
+ zdbug((LOG_DEBUG,"unauth ctrl_disp"));
+#endif
+ if (server == me_server)
+ clt_ack(notice, who, AUTH_FAILED);
+ return(ZERR_NONE);
+ }
+
+ /* the rest of the expected opcodes modify state; check for
+ unlocked host first */
+ host = hostm_find_host(&who->sin_addr);
+ if (host && host->zh_locked)
+ return(ZSRV_REQUEUE);
+
+ wantdefs = strcmp (opcode, CLIENT_SUBSCRIBE_NODEFS);
+ if (!wantdefs || !strcmp (opcode, CLIENT_SUBSCRIBE)) {
+ /* subscription notice */
+ if (!(client = client_which_client(who, notice))) {
+ if ((retval = client_register(notice,
+ who,
+ &client,
+ server,
+ wantdefs)) != ZERR_NONE)
+ {
+ syslog(LOG_NOTICE,
+ "subscr. register %s/%s/%d failed: %s",
+ notice->z_sender,
+ inet_ntoa(who->sin_addr),
+ ntohs(notice->z_port),
+ error_message(retval));
+ if (server == me_server) {
+ if (retval == ZSRV_BADSUBPORT) {
+ clt_ack(notice, who, AUTH_FAILED);
+ } else
+ hostm_deathgram(who, me_server);
+ }
+ return(ZERR_NONE);
+ }
+ if (!(client = client_which_client(who, notice))) {
+ syslog(LOG_CRIT, "subscr reg. failure");
+ abort();
+ }
+ }
+ if (strcmp (client->zct_principal->string, notice->z_sender)) {
+ /* you may only subscribe for your own clients */
+ if (server == me_server)
+ clt_ack(notice, who, AUTH_FAILED);
+ return(ZERR_NONE);
+ }
+#ifdef KERBEROS
+ /* in case it's changed */
+ (void) memcpy((caddr_t) client->zct_cblock, (caddr_t) ZGetSession(),
+ sizeof(C_Block));
+#endif
+ if ((retval = subscr_subscribe(client,notice)) != ZERR_NONE) {
+ syslog(LOG_WARNING, "subscr failed: %s",
+ error_message(retval));
+ if (server == me_server)
+ nack(notice, who);
+ return(ZERR_NONE);
+ }
+ } else if (!strcmp(opcode, CLIENT_UNSUBSCRIBE)) {
+ if ((client = client_which_client(who,notice)) != NULLZCNT) {
+ if (strcmp(client->zct_principal->string, notice->z_sender)) {
+ /* you may only cancel for your own clients */
+ if (server == me_server)
+ clt_ack(notice, who, AUTH_FAILED);
+ return(ZERR_NONE);
+ }
+#if 0
+ if (zdebug) {
+ if (server == me_server)
+ syslog (LOG_DEBUG,
+ "subscription cancel for %s/%d\n",
+ inet_ntoa (who->sin_addr),
+ ntohs (who->sin_port));
+ else
+ syslog (LOG_DEBUG,
+ "subscription cancel for %s/%d from %s\n",
+ inet_ntoa (who->sin_addr),
+ ntohs (who->sin_port),
+ server->addr);
+ }
+#endif
+ (void) subscr_cancel(who, notice);
+ } else {
+ nack(notice, who);
+ return(ZERR_NONE);
+ }
+ } else if (!strcmp(opcode, CLIENT_CANCELSUB)) {
+ /* canceling subscriptions implies I can punt info about
+ this client */
+ if ((client = client_which_client(who,notice)) != NULLZCNT) {
+ if (strcmp(client->zct_principal->string, notice->z_sender)) {
+ /* you may only cancel for your own clients */
+ if (server == me_server)
+ clt_ack(notice, who, AUTH_FAILED);
+ return(ZERR_NONE);
+ }
+ if (host) {
+ /* don't flush locations here, let him
+ do it explicitly */
+#if 0
+ zdbug((LOG_DEBUG, "cancelsub clt_dereg %s/%d",
+ inet_ntoa (who->sin_addr),
+ ntohs (who->sin_port)));
+#endif
+ hostm_lose_ignore(client);
+ (void) client_deregister(client, host, 0);
+ }
+
+ }
+ if (!client || !host) {
+#if 0
+ zdbug((LOG_DEBUG,"can_sub not found client"));
+#endif
+ if (server == me_server)
+ nack(notice, who);
+ return(ZERR_NONE);
+ }
+ } else {
+ syslog(LOG_WARNING, "unknown ctl opcode %s", opcode);
+ if (server == me_server)
+ nack(notice, who);
+ return(ZERR_NONE);
+ }
+
+ if (server == me_server) {
+ ack(notice, who);
+ server_forward(notice, auth, who);
+ }
+ return(ZERR_NONE);
+}
+
+