/* This file is part of the Project Athena Zephyr Notification System. * It contains functions for dumping server state between servers. * * Created by: John T. Kohl * * $Source$ * $Id$ * $Author$ * * Copyright (c) 1987,1988,1991 by the Massachusetts Institute of Technology. * For copying and distribution information, see the file * "mit-copyright.h". */ #include #ifndef lint #ifndef SABER static char rcsid_bdump_c[] = "$Id$"; #endif /* SABER */ #endif /* lint */ #include "zserver.h" #include #include #include /* for BSD */ /* inconsistent header files... */ #ifdef SignalIgnore #undef SIG_IGN #define SIG_IGN SignalIgnore #undef SIG_DFL #define SIG_DFL SignalDefault #endif /* * External functions are: * * void bdump_offer(who) * strut sockaddr_in *who; * * void bdump_send() * * void bdump_get(notice, auth, who, server) * ZNotice_t *notice; * int auth; * struct sockaddr_in *who; * ZServerDesc_t *server; * * Code_t bdump_send_list_tcp(kind, port, class, inst, opcode, * sender, recip, lyst, num) * ZNotice_Kind_t kind; * u_short port; * char *class, *inst, *opcode, *sender, *recip; * char *lyst[]; * int num; */ #ifdef __STDC__ # define P(s) s #else # define P(s) () #endif static void close_bdump P((void* arg)); static Code_t bdump_send_loop P((register ZServerDesc_t *server, char *vers)), bdump_ask_for P((char *inst)), bdump_recv_loop P((ZServerDesc_t *server)); static void bdump_get_v1 P((ZNotice_t *, int, struct sockaddr_in *, ZServerDesc_t *)); static void bdump_get_v1a P((ZNotice_t *notice, int auth, struct sockaddr_in *who, ZServerDesc_t *server)); static Code_t get_packet P((caddr_t packet, int len, int *retlen)); static Code_t extract_sin P((ZNotice_t *notice, struct sockaddr_in *target)); static Code_t send_done P((void)); static Code_t send_list P((ZNotice_Kind_t kind, int port, char *class_name, char *inst, char *opcode, char *sender, char *recip, char **lyst, int num)); static Code_t send_host_register P((ZHostList_t *host)); static Code_t sbd_loop P((struct sockaddr_in *from)); static Code_t gbd_loop P((ZServerDesc_t *server)); static Code_t send_normal_tcp P((ZNotice_Kind_t kind, int port, char *class_name, char *inst, char *opcode, char *sender, char *recip, char *message, int len)); static int net_read P((FILE *f, register char *buf, register int len)); static int net_write P((FILE *f, register char *buf, int len)); static int setup_file_pointers P((void)); static void shutdown_file_pointers P((void)); static void cleanup P((ZServerDesc_t *server)); #ifdef KERBEROS static int get_tgt P((void)); static long ticket_time; static char my_realm[REALM_SZ]; #define TKTLIFETIME 120 #define tkt_lifetime(val) ((long) val * 5L * 60L) #ifndef NOENCRYPTION C_Block serv_key; Sched serv_ksched; #endif #endif /* KERBEROS */ #undef P static timer bdump_timer; static int bdump_inited; static int live_socket = -1; static FILE *input, *output; static struct sockaddr_in bdump_sin; #ifdef notdef static int cancel_outgoing_dump; #endif int bdumping; extern char *bdump_version; /* * Functions for performing a brain dump between servers. */ /* * offer the brain dump to another server */ void bdump_offer(who) struct sockaddr_in *who; { Code_t retval; char buf[512], *addr, *lyst[2]; #ifndef KERBEROS int bdump_port = IPPORT_RESERVED - 1; #endif /* !KERBEROS */ #if 1 zdbug((LOG_DEBUG, "bdump_offer")); #endif #ifdef KERBEROS /* * when using Kerberos server-server authentication, we can * use any random local address */ if ((bdump_socket = socket(AF_INET, SOCK_STREAM, 0)) < 0) { syslog(LOG_ERR,"bdump_offer: socket: %m"); bdump_socket = -1; return; } (void) memset((caddr_t) &bdump_sin, 0, sizeof(bdump_sin)); /* a port field of 0 makes the UNIX * kernel choose an appropriate port/address pair */ bdump_sin.sin_port = 0; bdump_sin.sin_addr = my_addr; bdump_sin.sin_family = AF_INET; if ((retval = bind(bdump_socket, (struct sockaddr *) &bdump_sin, sizeof(bdump_sin))) < 0) { syslog(LOG_ERR, "bdump_offer: bind: %m"); (void) close(bdump_socket); bdump_socket = -1; return; } if (!bdump_sin.sin_port) { int len = sizeof(bdump_sin); if (getsockname(bdump_socket, (struct sockaddr *)&bdump_sin, &len)) { syslog(LOG_ERR, "bdump_offer: getsockname: %m"); (void) close(bdump_socket); bdump_socket = -1; return; } } #else /* !KERBEROS */ /* * when not using Kerberos, we can't use any old port, we use * Internet reserved ports instead (rresvport) */ if ((bdump_socket = rresvport(&bdump_port)) < 0) { syslog(LOG_ERR,"bdump_offer: socket: %m"); bdump_socket = -1; return; } (void) memset((caddr_t) &bdump_sin, 0, sizeof(bdump_sin)); bdump_sin.sin_port = htons((unsigned short)bdump_port); bdump_sin.sin_addr = my_addr; bdump_sin.sin_family = AF_INET; #endif /* KERBEROS */ (void) listen(bdump_socket, 1); bdump_timer = timer_set_rel(20L, close_bdump, (void *) 0); FD_SET(bdump_socket, &interesting); nfildes = max(bdump_socket, srv_socket) + 1; addr = inet_ntoa(bdump_sin.sin_addr); (void) sprintf(buf, "%d", ntohs(bdump_sin.sin_port)); lyst[0] = addr; lyst[1] = buf; if ((retval = ZSetDestAddr(who)) != ZERR_NONE) { syslog(LOG_WARNING, "bdump_offer: ZSetDestAddr: %s", error_message(retval)); return; } /* myname is the hostname */ /* the class instance is the version number, here it is */ /* bdump_version, which is set in main */ (void) send_list(ACKED, sock_sin.sin_port, ZEPHYR_ADMIN_CLASS, bdump_version, ADMIN_BDUMP, myname, "", lyst, 2); #if 1 zdbug((LOG_DEBUG,"bdump_offer: address is %s/%d\n", inet_ntoa(bdump_sin.sin_addr), ntohs(bdump_sin.sin_port))); #endif return; } /* * Accept a connection, and send the brain dump to the other server */ void #ifdef __STDC__ bdump_send(void) #else bdump_send() #endif { struct sockaddr_in from; ZServerDesc_t *server; Code_t retval; int fromlen = sizeof(from); int on = 1; #ifdef POSIX struct sigaction action; #endif #ifdef KERBEROS KTEXT_ST ticket; AUTH_DAT kdata; #else unsigned short fromport; #endif /* KERBEROS */ #if 1 zdbug((LOG_DEBUG, "bdump_send")); #endif /* accept the connection, and send the brain dump */ if ((live_socket = accept(bdump_socket, (struct sockaddr *)&from, &fromlen)) < 0) { syslog(LOG_ERR,"bdump_send: accept: %m"); return; } if (setsockopt(live_socket, SOL_SOCKET, SO_KEEPALIVE, (char *)&on, sizeof (on)) < 0) syslog(LOG_WARNING, "bdump_send: setsockopt (SO_KEEPALIVE): %m"); #ifndef KERBEROS fromport = ntohs(from.sin_port); #endif #ifdef POSIX (void) sigemptyset(&action.sa_mask); action.sa_flags = 0; action.sa_handler = SIG_IGN; (void) sigaction(SIGPIPE, &action, NULL); START_CRITICAL_CODE; #else (void) signal(SIGPIPE, SIG_IGN); /* so we can detect failures */ #endif from.sin_port = sock_sin.sin_port; /* we don't care what port it came from, and we need to fake out server_which_server() */ server = server_which_server(&from); if (!server) { syslog(LOG_ERR, "bdump_send: unknown server?"); server = limbo_server; } #if 1 zdbug((LOG_DEBUG, "bdump_send: connection from %s/%d", inet_ntoa (from.sin_addr), ntohs (from.sin_port))); #endif #ifdef notdef if (bdumping) { /* Already bdumping; punt one of the two. If this is a new host, punt this connection. If it's one we're already trying to talk to... arbitrary decision: The connection with the listener at the lower IP address will be punted. */ if (!server->zs_dumping) { zdbug ((LOG_INFO, "bdump_send: already dumping; breaking new bdump connection from %s", inet_ntoa (from.sin_addr))); (void) close (live_socket); return; } /* Should be safe now to get rid of listener socket. */ (void) close (bdump_socket); FD_CLR (bdump_socket, &interesting); bdump_socket = -1; timer_reset(bdump_timer); if (ntohl (bdump_sin.sin_addr.s_addr) < ntohl (from.sin_addr.s_addr)) { /* My address is lower; punt incoming connection. */ (void) close (live_socket); return; } else cancel_outgoing_dump = 1; } #endif bdumping = 1; server->zs_dumping = 1; if (bdump_socket >= 0) { /* shut down the listening socket and the timer */ FD_CLR(bdump_socket, &interesting); (void) close(bdump_socket); nfildes = srv_socket + 1; bdump_socket = -1; timer_reset(bdump_timer); } /* Now begin the brain dump. */ #ifdef KERBEROS /* receive the authenticator */ if ((retval = GetKerberosData(live_socket, from.sin_addr, &kdata, SERVER_SERVICE, ZEPHYR_SRVTAB)) != KSUCCESS) { syslog(LOG_ERR, "bdump_send: getkdata: %s", krb_err_txt[retval]); cleanup(server); return; } if (get_tgt()) { cleanup(server); return; } if (strcmp(kdata.pname,SERVER_SERVICE) || strcmp(kdata.pinst, SERVER_INSTANCE) || strcmp(kdata.prealm, my_realm)) { syslog(LOG_ERR, "bdump_send: peer not zephyr: %s.%s@%s", kdata.pname, kdata.pinst, kdata.prealm); cleanup(server); return; } /* authenticate back */ if ((retval = SendKerberosData(live_socket, &ticket, SERVER_SERVICE, SERVER_INSTANCE)) != 0) { syslog(LOG_ERR,"bdump_send: SendKerberosData: %s", error_message (retval)); cleanup(server); return; } #else /* !KERBEROS */ if ((fromport > IPPORT_RESERVED) || (fromport < (IPPORT_RESERVED / 2))) { syslog(LOG_ERR, "bdump_send: bad port from peer: %d", fromport); cleanup(server); return; } #endif /* KERBEROS */ if ((retval = setup_file_pointers()) != 0) { syslog (LOG_WARNING, "bdump_send: can't set up file pointers: %s", error_message (retval)); cleanup(server); return; } if ((retval = sbd_loop(&from)) != ZERR_NONE) { syslog(LOG_WARNING, "bdump_send: sbd_loop failed: %s", error_message(retval)); cleanup(server); return; } if ((retval = gbd_loop(server)) != ZERR_NONE) { syslog(LOG_WARNING, "bdump_send: gbd_loop failed: %s", error_message(retval)); cleanup(server); return; } #if 1 zdbug((LOG_DEBUG, "bdump_send: finished")); #endif if (server != limbo_server) { /* set this guy to be up, and schedule a hello */ server->zs_state = SERV_UP; timer_reset(server->zs_timer); server->zs_timer = timer_set_rel(0L, server_timo, (void *) server); } #if 0 zdbug((LOG_DEBUG,"cleanup sbd")); #endif shutdown_file_pointers (); #ifdef POSIX action.sa_handler = SIG_DFL; sigaction(SIGPIPE, &action, NULL); #else (void) signal(SIGPIPE, SIG_DFL); #endif bdump_inited = 1; bdumping = 0; server->zs_dumping = 0; #ifdef CONCURRENT /* Now that we are finished dumping, send all the queued packets */ server_send_queue(server); #endif END_CRITICAL_CODE; return; } /*ARGSUSED*/ static void bdump_get_v1_guts (notice, auth, who, server) ZNotice_t *notice; int auth; struct sockaddr_in *who; ZServerDesc_t *server; { struct sockaddr_in from; Code_t retval; int on = 1; #ifdef POSIX struct sigaction action; #endif #ifdef KERBEROS KTEXT_ST ticket; AUTH_DAT kdata; #else /* !KERBEROS */ int reserved_port = IPPORT_RESERVED - 1; #endif /* KERBEROS */ bdumping = 1; server->zs_dumping = 1; #ifdef POSIX action.sa_flags = 0; sigemptyset(&action.sa_mask); action.sa_handler = SIG_IGN; sigaction(SIGPIPE, &action, NULL); #else (void) signal(SIGPIPE, SIG_IGN); /* so we can detect problems */ #endif /* POSIX */ if (bdump_socket >= 0) { /* We cannot go get a brain dump when someone may potentially be connecting to us (if that other server is the server to whom we are connecting, we will deadlock. so we shut down the listening socket and the timer */ FD_CLR(bdump_socket, &interesting); (void) close(bdump_socket); nfildes = srv_socket+1; bdump_socket = -1; timer_reset(bdump_timer); } if ((retval = extract_sin(notice, &from)) != ZERR_NONE) { syslog(LOG_ERR, "bdump_get: sin: %s", error_message(retval)); #ifdef POSIX action.sa_handler = SIG_DFL; sigaction(SIGPIPE, &action, NULL); #else (void) signal(SIGPIPE, SIG_DFL); #endif bdumping = 0; server->zs_dumping = 0; return; } START_CRITICAL_CODE; #ifndef KERBEROS if (ntohs(from.sin_port) > IPPORT_RESERVED || ntohs(from.sin_port) < IPPORT_RESERVED / 2) { syslog(LOG_ERR, "bdump_get: port not reserved: %d", ntohs(from.sin_port)); cleanup(server); return; } live_socket = rresvport(&reserved_port); #else /* !KERBEROS */ live_socket = socket(AF_INET, SOCK_STREAM, 0); #endif /* KERBEROS */ if (live_socket < 0) { syslog(LOG_ERR, "bdump_get: socket: %m"); cleanup(server); return; } if (connect(live_socket, (struct sockaddr *) &from, sizeof(from))) { syslog(LOG_ERR, "bdump_get: connect: %m"); cleanup(server); return; } if (setsockopt(live_socket, SOL_SOCKET, SO_KEEPALIVE, (char *)&on, sizeof (on)) < 0) syslog(LOG_WARNING, "bdump_get: setsockopt (SO_KEEPALIVE): %m"); #if 1 zdbug((LOG_DEBUG, "bdump_get: connected")); #endif /* Now begin the brain dump. */ #ifdef KERBEROS /* send an authenticator */ if (get_tgt()) { cleanup(server); return; } if ((retval = SendKerberosData(live_socket, &ticket, SERVER_SERVICE, SERVER_INSTANCE)) != 0) { syslog(LOG_ERR,"bdump_get: %s", error_message (retval)); cleanup(server); return; } #if 1 zdbug((LOG_DEBUG, "bdump_get: SendKerberosData ok")); #endif /* get his authenticator */ if ((retval = GetKerberosData(live_socket, from.sin_addr, &kdata, SERVER_SERVICE, ZEPHYR_SRVTAB)) != KSUCCESS) { syslog(LOG_ERR, "bdump_get getkdata: %s",krb_err_txt[retval]); cleanup(server); return; } /* my_realm is filled in inside get_tgt() */ if (strcmp(kdata.pname, SERVER_SERVICE) || strcmp(kdata.pinst, SERVER_INSTANCE) || strcmp(kdata.prealm, my_realm)) { syslog(LOG_ERR, "bdump_get: peer not zephyr in lrealm: %s.%s@%s", kdata.pname, kdata.pinst,kdata.prealm); cleanup(server); return; } #endif /* KERBEROS */ if ((retval = setup_file_pointers()) != 0) { syslog (LOG_WARNING, "bdump_get: can't set up file pointers: %s", error_message (retval)); cleanup(server); return; } if ((retval = gbd_loop(server)) != ZERR_NONE) { syslog(LOG_WARNING, "bdump_get: gbd_loop failed: %s", error_message(retval)); cleanup(server); return; } #if 1 zdbug((LOG_DEBUG,"bdump_get: gbdl ok")); #endif if ((retval = sbd_loop(&from)) != ZERR_NONE) { syslog(LOG_WARNING, "sbd_loop failed: %s", error_message(retval)); cleanup(server); return; } #if 1 zdbug((LOG_DEBUG, "bdump_get: gbd finished")); #endif /* set this guy to be up, and schedule a hello */ server->zs_state = SERV_UP; timer_reset(server->zs_timer); server->zs_timer = timer_set_rel(0L, server_timo, (void *) server); #if 1 zdbug((LOG_DEBUG,"cleanup gbd")); #endif shutdown_file_pointers (); #ifdef POSIX action.sa_handler = SIG_DFL; sigaction(SIGPIPE, &action, NULL); #else (void) signal(SIGPIPE, SIG_DFL); #endif bdump_inited = 1; bdumping = 0; server->zs_dumping = 0; #ifdef CONCURRENT /* Now that we are finished dumping, send all the queued packets */ server_send_queue(server); #endif END_CRITICAL_CODE; return; } static void #ifdef __STDC__ bdump_get_v1(ZNotice_t *notice, int auth, struct sockaddr_in *who, ZServerDesc_t *server) #else bdump_get_v1(notice, auth, who, server) ZNotice_t *notice; int auth; struct sockaddr_in *who; ZServerDesc_t *server; #endif { if (bdump_socket >= 0) { /* We cannot go get a brain dump when someone may potentially be connecting to us (if that other server is the server to whom we are connecting, we will deadlock. so we shut down the listening socket and the timer */ FD_CLR(bdump_socket, &interesting); (void) close(bdump_socket); nfildes = srv_socket + 1; bdump_socket = -1; timer_reset(bdump_timer); } bdump_get_v1_guts (notice, auth, who, server); } static void #ifdef __STDC__ bdump_get_v1a( ZNotice_t *notice, int auth, struct sockaddr_in *who, ZServerDesc_t *server) #else bdump_get_v1a(notice, auth, who, server) ZNotice_t *notice; int auth; struct sockaddr_in *who; ZServerDesc_t *server; #endif { /* In version 1A, leave the listening file descriptor open; if we get a connection while we're dumping, one of the two will be punted. */ bdump_get_v1_guts (notice, auth, who, server); } void bdump_get(notice, auth, who, server) ZNotice_t *notice; int auth; struct sockaddr_in *who; ZServerDesc_t *server; { #ifdef __STDC__ # define P(s) s #else # define P(s) () #endif void (*proc) P((ZNotice_t *, int, struct sockaddr_in *, ZServerDesc_t *)); #undef P proc = NULL; #if 1 if (zdebug) syslog(LOG_DEBUG, "bdump_get: bdump v%s avail %s", notice->z_class_inst, inet_ntoa(who->sin_addr)); #endif /* * We do not listen to version "1" or "1A" because they are a * security threat (they send the CBlock in clear-text). */ if (!strcmp (notice->z_class_inst, "1.1") || !strcmp (notice->z_class_inst, "")) proc = bdump_get_v1; if (!strcmp (notice->z_class_inst, "1.1A")) proc = bdump_get_v1a; if (proc) (*proc) (notice, auth, who, server); else syslog(LOG_WARNING, "bdump_get: Incompatible bdump version '%s' from %s", notice->z_class_inst, inet_ntoa(who->sin_addr)); } /* * Send a list off as the specified notice */ int bdump_send_list_tcp(kind, port, class_name, inst, opcode, sender, recip, lyst, num) ZNotice_Kind_t kind; int port; char *class_name; char *inst; char *opcode; char *sender; char *recip; char **lyst; int num; { ZNotice_t notice; register ZNotice_t *pnotice = ¬ice; /* speed hack */ char *pack; int packlen, count; Code_t retval; u_short length; pnotice->z_kind = kind; pnotice->z_port = port; pnotice->z_class = class_name; pnotice->z_class_inst = inst; pnotice->z_opcode = opcode; pnotice->z_sender = sender; pnotice->z_recipient = recip; pnotice->z_default_format = ""; pnotice->z_num_other_fields = 0; if ((retval = ZFormatNoticeList(pnotice, (char **) lyst, num, &pack, &packlen, ZNOAUTH)) != ZERR_NONE) return(retval); length = htons((u_short) packlen); if ((count = net_write(output, (caddr_t) &length, sizeof(length))) != sizeof(length)) if (count < 0) { xfree(pack); /* free allocated storage */ return(errno); } else { syslog(LOG_WARNING, "slt (length) xmit: %d vs %d", sizeof(length),count); xfree(pack); /* free allocated storage */ return(ZSRV_PKSHORT); } if ((count = net_write(output, pack, packlen)) != packlen) if (count < 0) { xfree(pack); /* free allocated storage */ return(errno); } else { syslog(LOG_WARNING, "slt (packet) xmit: %d vs %d", packlen, count); xfree(pack); /* free allocated storage */ return(ZSRV_PKSHORT); } xfree(pack); /* free allocated storage */ return(ZERR_NONE); } static void shutdown_file_pointers () { if (input) { (void) fclose (input); input = 0; } if (output) { (void) fclose (output); output = 0; } if (live_socket >= 0) { (void) close (live_socket); live_socket = -1; } } static void cleanup(server) ZServerDesc_t *server; { #ifdef POSIX struct sigaction action; #endif #if 1 zdbug((LOG_DEBUG, "bdump cleanup")); #endif if (server != limbo_server) { server->zs_state = SERV_DEAD; timer_reset(server->zs_timer); server->zs_timer = timer_set_rel(0L, server_timo, (void *) server); } shutdown_file_pointers (); #ifdef POSIX action.sa_flags = 0; sigemptyset(&action.sa_mask); action.sa_handler = SIG_DFL; sigaction(SIGPIPE,&action, NULL); #else (void) signal(SIGPIPE, SIG_DFL); #endif /* POSIX */ bdumping = 0; server->zs_dumping = 0; #ifdef CONCURRENT /* XXX need to flush the server and the updates to it */ #endif END_CRITICAL_CODE; return; } #ifdef KERBEROS static int get_tgt() { int retval; #ifndef NOENCRYPTION Sched *s; #endif if (!*my_realm) if ((retval = krb_get_lrealm(my_realm, 1)) != KSUCCESS) { syslog(LOG_ERR,"krb_get_lrealm: %s", krb_err_txt[retval]); *my_realm = '\0'; return(1); } /* have they expired ? */ if (ticket_time < NOW - tkt_lifetime(TKTLIFETIME) + 15L) { /* +15 for leeway */ #if 0 zdbug((LOG_DEBUG,"get new tickets: %d %d %d", ticket_time, NOW, NOW - tkt_lifetime(TKTLIFETIME) + 15L)); #endif (void) dest_tkt(); { /* * XXX One version of krb_get_svc_in_tkt wants * this argument writable and at least INST_SZ * bytes long. */ static char buf[INST_SZ+1] = SERVER_INSTANCE; retval = krb_get_svc_in_tkt (SERVER_SERVICE, buf/*XXX*/, my_realm, SERVER_SERVICE, SERVER_INSTANCE, TKTLIFETIME, ZEPHYR_SRVTAB); } if (retval != KSUCCESS) { syslog(LOG_ERR,"get_tgt: krb_get_svc_in_tkt: %s", krb_err_txt[retval]); ticket_time = 0L; return(1); } else ticket_time = NOW; #ifndef NOENCRYPTION retval = read_service_key(SERVER_SERVICE, SERVER_INSTANCE, my_realm, 0 /*kvno*/, ZEPHYR_SRVTAB, serv_key); if (retval != KSUCCESS) { syslog(LOG_ERR, "get_tgt: read_service_key: %s", krb_err_txt[retval]); return 1; } s = (Sched *)check_key_sched_cache(serv_key); if (s) { serv_ksched = *s; } else { des_key_sched(serv_key, serv_ksched.s); add_to_key_sched_cache(serv_key, &serv_ksched); } #endif /* !NOENCRYPTION */ } return(0); } #endif /* KERBEROS */ static Code_t sbd_loop(from) struct sockaddr_in *from; { ZNotice_t bd_notice; ZPacket_t pack; ZServerDesc_t *server; int packlen = sizeof(pack); Code_t retval; struct sockaddr_in bogus_from; char *zeph_version = NULL; bogus_from = *from; bogus_from.sin_port = sock_sin.sin_port; while (1) { packlen = sizeof(pack); if ((retval = get_packet(pack, packlen, &packlen)) != ZERR_NONE) { syslog(LOG_ERR, "sbd_loop: notice get: %s", error_message(retval)); return(retval); } if ((retval = ZParseNotice(pack, packlen, &bd_notice)) != ZERR_NONE) { syslog(LOG_ERR, "sbd notice parse: %s", error_message(retval)); return(retval); } if (!zeph_version) { zeph_version = strsave(bd_notice.z_version); } #ifdef DEBUG if (zdebug) { char buf[4096]; (void) sprintf(buf, "bdump:%s '%s' '%s' '%s' '%s' '%s'", ZNoticeKinds[(int) bd_notice.z_kind], bd_notice.z_class, bd_notice.z_class_inst, bd_notice.z_opcode, bd_notice.z_sender, bd_notice.z_recipient); syslog(LOG_DEBUG, buf); } #endif /* DEBUG */ if (!strcmp(bd_notice.z_class_inst, ADMIN_LIMBO)) { /* he wants limbo */ #if 1 zdbug((LOG_DEBUG, "limbo req")); #endif if ((retval = bdump_send_loop(limbo_server, zeph_version)) != ZERR_NONE) return(retval); continue; } else if (!strcmp(bd_notice.z_class_inst, ADMIN_ME)) { /* he wants his state */ #if 1 zdbug((LOG_DEBUG, "his state req")); #endif if ((server = server_which_server(&bogus_from)) != NULLZSDT) { if ((retval = bdump_send_loop(server, zeph_version)) != ZERR_NONE) return(retval); } else { syslog(LOG_ERR,"sbd_loop: no state"); if ((retval = send_done()) != ZERR_NONE) return(retval); } continue; } else if (!strcmp(bd_notice.z_class_inst, ADMIN_YOU)) { /* he wants my state */ #if 1 zdbug((LOG_DEBUG, "my state req")); #endif if ((retval = bdump_send_loop(me_server, zeph_version)) != ZERR_NONE) return(retval); break; } else if (!strcmp(bd_notice.z_class_inst, ADMIN_DONE)) { break; } else { /* what does he want? */ #if 1 zdbug((LOG_DEBUG, "unknown req")); #endif break; } } if (zeph_version) xfree(zeph_version); return(ZERR_NONE); } static Code_t gbd_loop(server) ZServerDesc_t *server; { Code_t retval; /* * if we have no hosts in the 'limbo' state (on the limbo server), * ask for the other server to send us the limbo state. * Thus we keep track of all the hosts which haven't spoken in a while, * even in the face of server failure. */ if (otherservers[limbo_server_idx()].zs_hosts->q_forw == otherservers[limbo_server_idx()].zs_hosts) { if ((retval = bdump_ask_for(ADMIN_LIMBO)) != ZERR_NONE) return(retval); if ((retval = bdump_recv_loop(&otherservers[limbo_server_idx()])) != ZERR_NONE) return(retval); } /* Have I been given my own startup info yet? */ if (!bdump_inited) { if ((retval = bdump_ask_for(ADMIN_ME)) != ZERR_NONE) return(retval); if ((retval = bdump_recv_loop(me_server)) != ZERR_NONE) return(retval); } if ((retval = bdump_ask_for(ADMIN_YOU)) != ZERR_NONE) return(retval); retval = bdump_recv_loop(server); return(retval); } /* * The braindump offer wasn't taken, so we retract it. */ /*ARGSUSED*/ static void close_bdump(arg) void * arg; { if (bdump_socket >= 0) { FD_CLR(bdump_socket, &interesting); (void) close(bdump_socket); nfildes = srv_socket + 1; bdump_socket = -1; #if 1 zdbug((LOG_DEBUG, "bdump not used")); #endif } else { #if 1 zdbug((LOG_DEBUG, "bdump not open")); #endif } return; } /* * Ask the other server to send instruction packets for class instance * inst */ static Code_t bdump_ask_for(inst) char *inst; { Code_t retval; /* myname is the hostname */ retval = send_normal_tcp(ACKED, bdump_sin.sin_port, ZEPHYR_ADMIN_CLASS, inst, ADMIN_BDUMP, myname, "", (char *) NULL, 0); return(retval); } /* * Start receiving instruction notices from the brain dump socket */ static Code_t bdump_recv_loop(server) ZServerDesc_t *server; { ZNotice_t notice; ZPacket_t packet; int len; Code_t retval; ZClient_t *client = NULLZCNT; struct sockaddr_in current_who; int who_valid = 0; int flushing_subs = 0; #ifdef KERBEROS register char *cp; C_Block cblock; #endif /* KERBEROS */ #ifdef CONCURRENT fd_set readable, initial; int fd_ready; struct timeval tv; #endif /* CONCURRENT */ #if 1 zdbug((LOG_DEBUG, "bdump recv loop")); #endif #ifdef CONCURRENT FD_ZERO(&initial); FD_SET(srv_socket, &initial); #endif /* CONCURRENT */ /* do the inverse of bdump_send_loop, registering stuff on the fly */ while (1) { #ifdef CONCURRENT readable = initial; tv.tv_sec = tv.tv_usec = 0; if (msgs_queued()) { #if 1 zdbug((LOG_DEBUG, "brl msgqued")); #endif fd_ready = 1; } else fd_ready = select(srv_socket + 1, &readable, (fd_set *)0, (fd_set *)0, &tv); /* * if there are packets to be processed, do them. * We needn't worry about locking since we don't * know what's coming our way. */ if (fd_ready > 0) { #if 1 zdbug((LOG_DEBUG, "brl fdready")); #endif handle_packet(); #ifdef notdef if (cancel_outgoing_dump) { cancel_outgoing_dump = 0; return EWOULDBLOCK; /* maybe in a warped sort of way */ } #endif } else if (fd_ready < 0) syslog(LOG_ERR, "brl select: %m"); #endif /* CONCURRENT */ len = sizeof(packet); if ((retval = get_packet(packet, len, &len)) != ZERR_NONE) { syslog(LOG_ERR, "brl get pkt: %s", error_message(retval)); return(retval); } if ((retval = ZParseNotice(packet, len, ¬ice)) != ZERR_NONE) { syslog(LOG_ERR, "brl notice parse: %s", error_message(retval)); return(retval); } #if defined (DEBUG) if (zdebug) { char buf[4096]; (void) sprintf(buf, "bdump:%s '%s' '%s' '%s' '%s' '%s'", ZNoticeKinds[(int) notice.z_kind], notice.z_class, notice.z_class_inst, notice.z_opcode, notice.z_sender, notice.z_recipient); syslog(LOG_DEBUG, buf); } #endif /* DEBUG */ if (notice.z_kind == HMCTL) { /* host register */ if ((retval = extract_sin(¬ice, ¤t_who)) != ZERR_NONE) { syslog(LOG_ERR, "brl hmctl sin: %s", error_message(retval)); return(retval); } who_valid = 1; /* 1 = tell it we are authentic */ if ((retval = hostm_dispatch(¬ice, 1, ¤t_who, server)) != ZERR_NONE) { syslog(LOG_ERR,"brl hm_disp failed: %s", error_message(retval)); return(retval); } } else if (!strcmp(notice.z_opcode, ADMIN_DONE)) { /* end of brain dump */ return(ZERR_NONE); } else if (!who_valid) { syslog(LOG_ERR, "brl: no current host"); return(ZSRV_HNOTFOUND); } else if (!strcmp(notice.z_class, LOGIN_CLASS)) { /* 1 = tell it we are authentic */ if ((retval = ulogin_dispatch(¬ice, 1, ¤t_who, server)) != ZERR_NONE) { syslog(LOG_ERR, "brl ul_disp failed: %s", error_message(retval)); return(retval); } } else if (!strcmp(notice.z_opcode, ADMIN_NEWCLT)) { /* register a new client */ notice.z_port = htons((u_short)atoi(notice.z_message)); if (ntohs(notice.z_port) == 0) { /* this is a bogus client from an older rev. server, so we just flush it. */ syslog(LOG_ERR, "brl flushing %s/0", inet_ntoa(current_who.sin_addr)); flushing_subs = 1; continue; /* while loop */ } flushing_subs = 0; if ((retval = client_register(¬ice, ¤t_who, &client, server, 0)) != ZERR_NONE) { syslog(LOG_ERR,"brl register failed: %s", error_message(retval)); return(retval); } #ifdef KERBEROS (void) memset((caddr_t) client->zct_cblock, 0, sizeof(C_Block)); if (*notice.z_class_inst) { /* a C_Block is there */ cp = notice.z_message + strlen(notice.z_message) + 1; retval = ZReadAscii(cp,strlen(cp), cblock, sizeof(C_Block)); if (retval != ZERR_NONE) { syslog(LOG_ERR,"brl bad cblk read: %s (%s)", error_message(retval), cp); } else { #ifdef NOENCRYPTION memcpy((caddr_t)cblock, (caddr_t)client->zct_cblock, sizeof(C_Block)); #else des_ecb_encrypt(cblock, client->zct_cblock, serv_ksched.s, DES_DECRYPT); #endif } } #endif /* KERBEROS */ } else if (!strcmp(notice.z_opcode, CLIENT_SUBSCRIBE)) { if (flushing_subs) continue; /* while loop */ /* a subscription packet */ if (!client) { syslog(LOG_ERR, "brl no client"); return(ZSRV_NOCLT); } if ((retval = subscr_subscribe(client, ¬ice)) != ZERR_NONE) { syslog(LOG_WARNING, "brl subscr failed: %s", error_message(retval)); return(retval); } } else { syslog(LOG_ERR, "brl bad opcode %s",notice.z_opcode); return(ZSRV_UNKNOWNOPCODE); } } } /* * Send all the state from server to the peer. */ static Code_t bdump_send_loop(server, vers) ZServerDesc_t *server; char *vers; { register ZHostList_t *host; register ZClientList_t *clist; Code_t retval; #ifdef CONCURRENT fd_set readable, initial; int fd_ready; struct timeval tv; #endif /* CONCURRENT */ #if 1 zdbug((LOG_DEBUG, "bdump send loop")); #endif #ifdef CONCURRENT FD_ZERO(&initial); FD_SET(srv_socket, &initial); #endif /* CONCURRENT */ for (host = server->zs_hosts->q_forw; host != server->zs_hosts; host = host->q_forw) { /* for each host */ #ifdef CONCURRENT host->zh_locked = 1; readable = initial; tv.tv_sec = tv.tv_usec = 0; if (msgs_queued()) fd_ready = 1; else fd_ready = select(srv_socket + 1, &readable, (fd_set *)0, (fd_set *)0, &tv); /* * if there are packets to be processed, do them. * locking the host above insures nothing we are working on * gets trashed. */ if (fd_ready > 0) { handle_packet(); #ifdef notdef if (cancel_outgoing_dump) { cancel_outgoing_dump = 0; return EWOULDBLOCK; } #endif } else if (fd_ready < 0) syslog(LOG_ERR, "bsl select: %m"); #endif /* CONCURRENT */ if ((retval = send_host_register(host)) != ZERR_NONE) { host->zh_locked = 0; return(retval); } if ((retval = uloc_send_locations(host, vers)) != ZERR_NONE) { host->zh_locked = 0; return(retval); } if (!host->zh_clients) { host->zh_locked = 0; continue; } for (clist = host->zh_clients->q_forw; clist != host->zh_clients; clist = clist->q_forw) { /* for each client */ if (!clist->zclt_client->zct_subs) { host->zh_locked = 0; continue; } if ((retval = subscr_send_subs(clist->zclt_client, vers)) != ZERR_NONE) { host->zh_locked = 0; return(retval); } } host->zh_locked = 0; } retval = send_done(); return(retval); } /* * Send a host boot packet to the other server */ static Code_t send_host_register(host) ZHostList_t *host; { char buf[512], *addr, *lyst[2]; Code_t retval; #if 0 zdbug((LOG_DEBUG, "bdump_host_register")); #endif addr = inet_ntoa(host->zh_addr.sin_addr); (void) sprintf(buf, "%d", ntohs(host->zh_addr.sin_port)); lyst[0] = addr; lyst[1] = buf; /* myname is the hostname */ retval = bdump_send_list_tcp (HMCTL, (int) bdump_sin.sin_port, ZEPHYR_CTL_CLASS, ZEPHYR_CTL_HM, HM_BOOT, myname, "", lyst, 2); if (retval != ZERR_NONE) syslog(LOG_ERR, "shr send: %s",error_message(retval)); return(retval); } /* * Send a sync indicating end of this host */ static Code_t send_done() { Code_t retval; #if 1 zdbug((LOG_DEBUG, "send_done")); #endif retval = send_normal_tcp(SERVACK, bdump_sin.sin_port, ZEPHYR_ADMIN_CLASS, "", ADMIN_DONE, myname, "", (char *) NULL, 0); return(retval); } /* * Send a list off as the specified notice */ static Code_t send_list(kind, port, class_name, inst, opcode, sender, recip, lyst, num) ZNotice_Kind_t kind; int port; char *class_name; char *inst; char *opcode; char *sender; char *recip; char **lyst; int num; { ZNotice_t notice; register ZNotice_t *pnotice; /* speed hack */ char *pack; int packlen; Code_t retval; pnotice = ¬ice; pnotice->z_kind = kind; pnotice->z_port = port; pnotice->z_class = class_name; pnotice->z_class_inst = inst; pnotice->z_opcode = opcode; pnotice->z_sender = sender; pnotice->z_recipient = recip; pnotice->z_default_format = ""; pnotice->z_num_other_fields = 0; if ((retval = ZFormatNoticeList(pnotice, lyst, num, &pack, &packlen, ZNOAUTH)) != ZERR_NONE) { syslog(LOG_WARNING, "sl format: %s", error_message(retval)); return(retval); } if ((retval = ZSendPacket(pack, packlen, 0)) != ZERR_NONE) { syslog(LOG_WARNING, "sl xmit: %s", error_message(retval)); xfree(pack); /* free allocated storage */ return(retval); } xfree(pack); /* free allocated storage */ return(ZERR_NONE); } /* * Send a message off as the specified notice, via TCP */ static Code_t send_normal_tcp(kind, port, class_name, inst, opcode, sender, recip, message, len) ZNotice_Kind_t kind; int port; char *class_name; char *inst; char *opcode; char *sender; char *recip; char *message; int len; { ZNotice_t notice; register ZNotice_t *pnotice; /* speed hack */ char *pack; int packlen, count; Code_t retval; u_short length; pnotice = ¬ice; pnotice->z_kind = kind; pnotice->z_port = port; pnotice->z_class = class_name; pnotice->z_class_inst = inst; pnotice->z_opcode = opcode; pnotice->z_sender = sender; pnotice->z_recipient = recip; pnotice->z_default_format = ""; pnotice->z_message = message; pnotice->z_message_len = len; pnotice->z_num_other_fields = 0; if ((retval = ZFormatNotice(pnotice, &pack, &packlen, ZNOAUTH)) != ZERR_NONE) { syslog(LOG_WARNING, "sn format: %s", error_message(retval)); return(retval); } length = htons((u_short) packlen); if ((count = net_write(output, (caddr_t) &length, sizeof(length))) != sizeof(length)) { if (count < 0) { syslog(LOG_WARNING, "snt xmit/len: %m"); xfree(pack); /* free allocated storage */ return(errno); } else { syslog(LOG_WARNING, "snt xmit: %d vs %d",sizeof(length),count); xfree(pack); /* free allocated storage */ return(ZSRV_LEN); } } if ((count = net_write(output, pack, packlen)) != packlen) if (count < 0) { syslog(LOG_WARNING, "snt xmit: %m"); xfree(pack); /* free allocated storage */ return(errno); } else { syslog(LOG_WARNING, "snt xmit: %d vs %d",packlen, count); xfree(pack); /* free allocated storage */ return(ZSRV_LEN); } xfree(pack); /* free allocated storage */ return(ZERR_NONE); } /* * get a packet from the TCP socket * return 0 if successful, error code else */ static Code_t get_packet(packet, len, retlen) caddr_t packet; int len; int *retlen; { u_short length; int result; if ((result = net_read(input, (caddr_t) &length, sizeof(u_short))) < sizeof(short)) { if (result < 0) return(errno); else { syslog(LOG_ERR, "get_pkt len: %d vs %d (%m)", result, sizeof(short)); return(ZSRV_LEN); } } length = ntohs(length); if (len < length) return(ZSRV_BUFSHORT); if ((result = net_read(input, packet, (int) length)) < length) { if (result < 0) return(errno); else { syslog(LOG_ERR, "get_pkt: %d vs %d (%m)",result, length); return(ZSRV_LEN); } } *retlen = (int) length; return(ZERR_NONE); } static Code_t extract_sin(notice, target) ZNotice_t *notice; struct sockaddr_in *target; { register char *cp = notice->z_message; char *buf; buf = cp; if (!notice->z_message_len || *buf == '\0') { #if 0 zdbug((LOG_DEBUG,"no addr")); #endif return(ZSRV_PKSHORT); } target->sin_addr.s_addr = inet_addr(cp); cp += (strlen(cp) + 1); /* past the null */ if ((cp >= notice->z_message + notice->z_message_len) || (*cp == '\0')) { #if 0 zdbug((LOG_DEBUG, "no port")); #endif return(ZSRV_PKSHORT); } target->sin_port = htons((u_short) atoi(cp)); target->sin_family = AF_INET; return(ZERR_NONE); } static int net_read(f, buf, len) FILE *f; register char *buf; register int len; { int cc, len2 = 0; fflush (output); do { errno = 0; cc = fread (buf, 1, len, f); if (cc == 0) return -1; buf += cc; len2 += cc; len -= cc; } while (len > 0); return len2; } static int net_write(f, buf, len) FILE *f; register char *buf; int len; { int cc; register int wrlen = len; do { cc = fwrite (buf, 1, wrlen, f); if (cc == 0) return -1; buf += cc; wrlen -= cc; } while (wrlen > 0); return len; } static int setup_file_pointers () { int fd; input = fdopen (live_socket, "r"); if (!input) return errno; fd = dup (live_socket); if (fd < 0) return errno; output = fdopen (fd, "w"); if (!output) return errno; return 0; }