diff options
author | 1994-11-13 02:28:50 +0000 | |
---|---|---|
committer | 1994-11-13 02:28:50 +0000 | |
commit | b829891a761b11c6106bb277ec04af79850c8c50 (patch) | |
tree | aa7c590006f94fed924bdc0970b2b3057c8f4324 /server/dispatch.c.auth | |
parent | 07c6c399486e93589a1cfcffcc972c5fae93c35a (diff) |
Initial revision
Diffstat (limited to 'server/dispatch.c.auth')
-rw-r--r-- | server/dispatch.c.auth | 1078 |
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(¬ice->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, ¬ice->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); +} + + |