]> asedeno.scripts.mit.edu Git - 1ts-debian.git/blob - zephyr/server/bdump.c
9efc3b5e8897ada5d25d6884bd06014f6ca8a7df
[1ts-debian.git] / zephyr / server / bdump.c
1 /* This file is part of the Project Athena Zephyr Notification System.
2  * It contains functions for dumping server state between servers.
3  *
4  *      Created by:     John T. Kohl
5  *
6  *      $Source: /afs/dev.mit.edu/source/repository/athena/lib/zephyr/server/bdump.c,v $
7  *      $Id$
8  *      $Author$
9  *
10  *      Copyright (c) 1987,1988,1991 by the Massachusetts Institute of Technology.
11  *      For copying and distribution information, see the file
12  *      "mit-copyright.h". 
13  */
14
15 #include <zephyr/mit-copyright.h>
16 #include "zserver.h"
17 #include <sys/socket.h>
18 #include <com_err.h>
19
20 #ifndef lint
21 static const char rcsid_bdump_c[] = "$Id$";
22 #endif /* lint */
23
24 /*
25  * External functions are:
26  *
27  * void bdump_offer(who)
28  *      strut sockaddr_in *who;
29  *
30  * void bdump_send()
31  *
32  * void bdump_get(notice, auth, who, server)
33  *      ZNotice_t *notice;
34  *      int auth;
35  *      struct sockaddr_in *who;
36  *      Server *server;
37  *
38  * Code_t bdump_send_list_tcp(kind, port, class, inst, opcode,
39  *                          sender, recip, lyst, num)
40  *      ZNotice_Kind_t kind;
41  *      u_short port;
42  *      char *class, *inst, *opcode, *sender, *recip;
43  *      char *lyst[];
44  *      int num;
45  */
46
47 #if defined(HAVE_KRB5) && 0
48 int krb5_init_keyblock(krb5_context context,
49         krb5_enctype type,
50         size_t size,
51         krb5_keyblock **akey)
52 {
53 krb5_error_code ret;
54 size_t len;
55 krb5_keyblock *key;
56
57 *akey=NULL;
58 key=malloc(sizeof(*key));
59 memset(key, 0, sizeof(*key));
60 ret = krb5_enctype_keysize(context, type, &len);
61 if (ret)
62 return ret;
63
64 if (len != size) {
65 krb5_set_error_string(context, "Encryption key %d is %lu bytes "
66 "long, %lu was passed in",
67 type, (unsigned long)len, (unsigned long)size);
68 return KRB5_PROG_ETYPE_NOSUPP;
69 }
70
71 ret = krb5_data_alloc(&key->keyvalue, len);
72 if(ret) {
73 krb5_set_error_string(context, "malloc failed: %lu",
74 (unsigned long)len);
75 return ret;
76 }
77 key->keytype = type;
78 *akey=key;
79 return 0;
80 }
81 #endif
82
83
84 static void close_bdump(void* arg);
85 static Code_t bdump_send_loop(Server *server);
86 static Code_t bdump_ask_for(char *inst);
87 static Code_t bdump_recv_loop(Server *server);
88 static void bdump_get_v12(ZNotice_t *, int, struct sockaddr_in *,
89                                Server *);
90 static Code_t get_packet(void *packet, int len, int *retlen);
91 static Code_t extract_sin(ZNotice_t *notice, struct sockaddr_in *target);
92 static Code_t send_done(void);
93 static Code_t send_list(ZNotice_Kind_t kind, int port, char *class_name,
94                              char *inst, char *opcode, char *sender,
95                              char *recip, char **lyst, int num);
96 static Code_t send_normal_tcp(ZNotice_Kind_t kind, int port,
97                                    char *class_name,
98                                    char *inst, char *opcode, char *sender,
99                                    char *recip, char *message, int len);
100 static int net_read(FILE *f, char *buf, int len);
101 static int net_write(FILE *f, char *buf, int len);
102 static int setup_file_pointers(void);
103 static void shutdown_file_pointers(void);
104 static void cleanup(Server *server);
105
106 #ifdef HAVE_KRB5
107 static long ticket5_time;
108 #define TKT5LIFETIME 8*60*60
109 #define tkt5_lifetime(val) (val)
110 #endif
111
112 #ifdef HAVE_KRB4
113 static long ticket_time;
114
115 #define TKTLIFETIME     120
116 #define tkt_lifetime(val) ((long) val * 5L * 60L)
117
118 #ifndef NOENCRYPTION
119 extern C_Block  serv_key;
120 extern Sched    serv_ksched;
121 #endif
122 #endif /* HAVE_KRB4 */
123
124 static Timer *bdump_timer;
125 static int live_socket = -1;
126 static FILE *input, *output;
127 static struct sockaddr_in bdump_sin;
128 #ifdef HAVE_KRB5
129 static krb5_auth_context bdump_ac;
130 #endif
131 #ifdef notdef
132 static int cancel_outgoing_dump;
133 #endif
134
135 int bdumping;
136 int bdump_concurrent;
137 extern char *bdump_version;
138 extern int bdump_auth_proto;
139
140 /*
141  * Functions for performing a brain dump between servers.
142  */
143
144 /*
145  * offer the brain dump to another server
146  */
147
148 void
149 bdump_offer(struct sockaddr_in *who)
150 {
151     Code_t retval;
152     char buf[512], *addr, *lyst[2];
153 #ifndef HAVE_KRB4
154     int bdump_port = IPPORT_RESERVED - 1;
155 #endif /* !HAVE_KRB4 */
156 #if 1
157     zdbug((LOG_DEBUG, "bdump_offer"));
158 #endif
159 #if defined(HAVE_KRB4) || defined(HAVE_KRB5)
160     /* 
161      * when using kerberos server-server authentication, we can
162      * use any random local address 
163      */
164     bdump_socket = socket(AF_INET, SOCK_STREAM, 0);
165     if (bdump_socket < 0) {
166         syslog(LOG_ERR,"bdump_offer: socket: %m");
167         bdump_socket = -1;
168         return;
169     }
170     memset(&bdump_sin, 0, sizeof(bdump_sin));
171     /* a port field of 0 makes the UNIX
172      * kernel choose an appropriate port/address pair */
173  
174     bdump_sin.sin_port = 0;
175     bdump_sin.sin_addr = my_addr;
176     bdump_sin.sin_family = AF_INET;
177     retval = bind(bdump_socket, (struct sockaddr *) &bdump_sin,
178                   sizeof(bdump_sin));
179     if (retval < 0) {
180         syslog(LOG_ERR, "bdump_offer: bind: %m");
181         close(bdump_socket);
182         bdump_socket = -1;
183         return;
184     }
185     if (!bdump_sin.sin_port) {
186         int len = sizeof(bdump_sin);
187
188         if (getsockname(bdump_socket,
189                         (struct sockaddr *) &bdump_sin, &len) < 0) {
190             syslog(LOG_ERR, "bdump_offer: getsockname: %m");
191             close(bdump_socket);
192             bdump_socket = -1;
193             return;
194         }
195     }
196 #else  /* !HAVE_KRB4 */
197     /*
198      * when not using HAVE_KRB4, we can't use any old port, we use
199      * Internet reserved ports instead (rresvport)
200      */
201     bdump_socket = rresvport(&bdump_port);
202     if (bdump_socket < 0) {
203         syslog(LOG_ERR,"bdump_offer: socket: %m");
204         bdump_socket = -1;
205         return;
206     }
207     memset(&bdump_sin, 0, sizeof(bdump_sin));
208     bdump_sin.sin_port = htons((unsigned short) bdump_port);
209     bdump_sin.sin_addr = my_addr;
210     bdump_sin.sin_family = AF_INET;
211 #endif                          /* HAVE_KRB4 */
212
213     listen(bdump_socket, 1);
214  
215     bdump_timer = timer_set_rel(20L, close_bdump, NULL);
216     FD_SET(bdump_socket, &interesting);
217     nfds = max(bdump_socket, srv_socket) + 1;
218
219     addr = inet_ntoa(bdump_sin.sin_addr);
220     sprintf(buf, "%d", ntohs(bdump_sin.sin_port));
221     lyst[0] = addr;
222     lyst[1] = buf;
223  
224     retval = ZSetDestAddr(who);
225     if (retval != ZERR_NONE) {
226         syslog(LOG_WARNING, "bdump_offer: ZSetDestAddr: %s",
227                error_message(retval));
228         return;
229     }
230  
231     /* myname is the hostname */
232     /* the class instance is the version number, here it is */
233     /* bdump_version, which is set in main */
234     send_list(ACKED, srv_addr.sin_port, ZEPHYR_ADMIN_CLASS, bdump_version,
235               ADMIN_BDUMP, myname, "", lyst, 2);
236         
237 #if 1
238     zdbug((LOG_DEBUG,"bdump_offer: address is %s/%d\n",
239            inet_ntoa(bdump_sin.sin_addr),
240            ntohs(bdump_sin.sin_port)));
241 #endif
242     return;
243 }
244
245 /*
246  * Accept a connection, and send the brain dump to the other server
247  */
248
249 void
250 bdump_send(void)
251 {
252     struct sockaddr_in from;
253     Server *server;
254     Code_t retval;
255     int fromlen = sizeof(from);
256     int on = 1;
257 #ifdef _POSIX_VERSION
258     struct sigaction action;
259 #endif
260 #if defined(HAVE_KRB4) || defined(HAVE_KRB5)
261     char *data = NULL;
262     int len = 0;
263     int proto = 0;
264 #endif
265 #ifdef HAVE_KRB4
266     KTEXT_ST ticket;
267     AUTH_DAT kdata;
268     /* may be moved into kstuff.c */
269     char instance [INST_SZ];
270 #endif
271 #ifdef HAVE_KRB5
272     /* may be moved into kstuff.c */
273     krb5_principal principal;
274     krb5_data k5data;
275     krb5_ap_rep_enc_part *rep;
276     krb5_keytab kt;
277 #endif
278 #if !defined(HAVE_KRB4) && !defined(HAVE_KRB5)
279     unsigned short fromport;
280 #endif /* HAVE_KRB4 */
281  
282 #if 1
283     zdbug((LOG_DEBUG, "bdump_send"));
284 #endif
285     /* accept the connection, and send the brain dump */
286     live_socket = accept(bdump_socket, (struct sockaddr *) &from, &fromlen);
287     if (live_socket < 0) {
288         syslog(LOG_ERR,"bdump_send: accept: %m");
289         return;
290     }
291     if (setsockopt(live_socket, SOL_SOCKET, SO_KEEPALIVE, (char *) &on,
292                    sizeof(on)) < 0)
293         syslog(LOG_WARNING, "bdump_send: setsockopt (SO_KEEPALIVE): %m");
294  
295 #ifndef HAVE_KRB4
296     fromport = ntohs(from.sin_port);
297 #endif
298  
299 #ifdef _POSIX_VERSION
300     sigemptyset(&action.sa_mask);
301     action.sa_flags = 0;
302     action.sa_handler = SIG_IGN;
303     sigaction(SIGPIPE, &action, NULL);
304
305 #else
306     signal(SIGPIPE, SIG_IGN);   /* so we can detect failures */
307 #endif
308  
309     from.sin_port = srv_addr.sin_port; /* we don't care what port
310                                         * it came from, and we need to
311                                         * fake out server_which_server() */
312     server = server_which_server(&from);
313     if (!server) {
314         syslog(LOG_ERR, "bdump_send: unknown server?");
315         server = limbo_server;
316     }
317 #if 1
318     zdbug((LOG_DEBUG, "bdump_send: connection from %s/%d",
319            inet_ntoa(from.sin_addr), ntohs(from.sin_port)));
320 #endif
321
322     bdumping = 1;
323     server->dumping = 1;
324
325     if (bdump_socket >= 0) {
326         /* shut down the listening socket and the timer. */
327         FD_CLR(bdump_socket, &interesting);
328         close(bdump_socket);
329         nfds = srv_socket + 1;
330         bdump_socket = -1;
331         timer_reset(bdump_timer);
332     }
333
334     /* Now begin the brain dump. */
335 #if defined(HAVE_KRB5) || defined(HAVE_KRB4)
336     retval = ReadKerberosData(live_socket, &len, &data, &proto);
337
338     if (retval != 0) {
339         syslog(LOG_ERR, "bdump_send: ReadKerberosData: %s",
340                krb_get_err_text(retval));
341         cleanup(server);
342         return;
343     }
344
345     syslog(LOG_INFO, "bdump_send: got %d bytes of authenticator for protocol %d", len, proto);
346
347     if (get_tgt()) {
348         syslog(LOG_ERR, "bdump_send: get_tgt failed");
349         cleanup(server);
350         return;
351     }
352  
353     switch(proto) {
354 #ifdef HAVE_KRB5
355     case 5:
356         /* "server" side */
357         retval = krb5_build_principal(Z_krb5_ctx, &principal, 
358                                       strlen(ZGetRealm()),
359                                       ZGetRealm(),
360                                       SERVER_KRB5_SERVICE, SERVER_INSTANCE,
361                                       0); 
362         if (retval) {
363             syslog(LOG_ERR, "bdump_send: krb5_build_principal: %s", error_message(retval));
364             cleanup(server);
365             return;
366         }
367         
368
369         retval = krb5_auth_con_init(Z_krb5_ctx, &bdump_ac);
370         if (retval) {
371             syslog(LOG_ERR, "bdump_send: krb5_auth_con_init: %s", error_message(retval));
372             cleanup(server);
373             return;
374         }
375
376         retval = krb5_auth_con_setflags(Z_krb5_ctx, bdump_ac, KRB5_AUTH_CONTEXT_DO_SEQUENCE);
377         if (retval) {
378             syslog(LOG_ERR, "bdump_send: krb5_auth_con_setflags: %s", error_message(retval));
379             cleanup(server);
380             return;
381         }
382
383         retval = krb5_auth_con_genaddrs(Z_krb5_ctx, bdump_ac, live_socket,
384                                        KRB5_AUTH_CONTEXT_GENERATE_LOCAL_FULL_ADDR|KRB5_AUTH_CONTEXT_GENERATE_REMOTE_FULL_ADDR);
385         if (retval) {
386             syslog(LOG_ERR, "bdump_send: krb5_auth_con_genaddrs: %s", error_message(retval));
387             cleanup(server);
388             return;
389         }
390
391         /* Get the "client" krb_ap_req */
392
393         memset((char *)&k5data, 0, sizeof(krb5_data));
394         k5data.length = len;
395         k5data.data = data;
396         if (retval) {
397              syslog(LOG_ERR, "bdump_send: cannot get auth response: %s",
398                     error_message(retval)); 
399              cleanup(server);
400              return;
401         }
402
403         /* resolve keytab */
404         retval = krb5_kt_resolve(Z_krb5_ctx, keytab_file, &kt);
405         if (retval) {
406             syslog(LOG_ERR, "bdump_send: cannot resolve keytab: %s", 
407                    error_message(retval));
408             krb5_kt_close(Z_krb5_ctx, kt);
409             cleanup(server);
410             return;
411         }
412
413         retval = krb5_rd_req(Z_krb5_ctx, &bdump_ac, &k5data, principal, kt, NULL, NULL);
414         krb5_free_principal(Z_krb5_ctx, principal);
415         krb5_kt_close(Z_krb5_ctx, kt);
416         free(k5data.data);
417         memset((char *)&k5data, 0, sizeof(krb5_data));
418         if (retval) {
419              syslog(LOG_ERR, "bdump_send: mutual authentication failed: %s",
420                     error_message(retval));
421              cleanup(server);
422              return;
423         }
424
425         /* Now send back our auth packet */
426
427         retval = krb5_mk_rep(Z_krb5_ctx, bdump_ac, &k5data);
428         if (retval) {
429             syslog(LOG_ERR, "bdump_send: krb5_mk_rep: %s", error_message(retval));
430             cleanup(server);
431             return;
432         }
433         retval = SendKrb5Data(live_socket, &k5data);
434         if (retval) {
435              syslog(LOG_ERR, "bdump_send: cannot send authenticator: %s",
436                     error_message(retval));
437              krb5_free_data_contents(Z_krb5_ctx, &k5data);
438              cleanup(server);
439              return;
440         }    
441         krb5_free_data_contents(Z_krb5_ctx, &k5data);
442         break;
443 #endif  /* HAVE_KRB5 */  
444 #ifdef HAVE_KRB4
445     case 4:
446         /* here to krb_rd_req from GetKerberosData candidate for refactoring
447            back into kstuff.c */
448         (void) strcpy(instance, "*");           /* let Kerberos fill it in */
449
450         ticket.length = len;
451         memcpy(&ticket.dat, data, MIN(len, sizeof(ticket.dat)));
452         retval = krb_rd_req(&ticket, SERVER_SERVICE, instance,
453                             from.sin_addr.s_addr, &kdata, srvtab_file);
454         /*
455         retval = GetKerberosData(live_socket, from.sin_addr, &kdata,
456                                  SERVER_SERVICE, srvtab_file);
457         */
458         if (retval != KSUCCESS) {
459             syslog(LOG_ERR, "bdump_send: getkdata: %s",
460                    krb_get_err_text(retval));
461             cleanup(server);
462             return;
463         }
464         if (strcmp(kdata.pname, SERVER_SERVICE) ||
465             strcmp(kdata.pinst, SERVER_INSTANCE) ||
466             strcmp(kdata.prealm, ZGetRealm())) {
467             syslog(LOG_ERR, "bdump_send: peer not zephyr: %s.%s@%s",
468                    kdata.pname, kdata.pinst, kdata.prealm);
469             cleanup(server);
470             return;
471         }
472         /* authenticate back */
473         retval = SendKerberosData(live_socket, &ticket, SERVER_SERVICE,
474                                   SERVER_INSTANCE);
475         if (retval != 0) {
476             syslog(LOG_ERR,"bdump_send: SendKerberosData: %s",
477                    error_message (retval));
478             cleanup(server);
479             return;
480         }
481         break;
482 #endif /* HAVE_KRB4 */
483     }
484 #else /* HAVE_KRB4 || HAVE_KRB5 */
485     if (fromport > IPPORT_RESERVED || fromport < IPPORT_RESERVED / 2) {
486         syslog(LOG_ERR, "bdump_send: bad port from peer: %d", fromport);
487         cleanup(server);
488         return;
489     }
490 #endif /* HAVE_KRB4 || HAVE_KRB5 */
491     retval = setup_file_pointers();
492     if (retval != 0) {
493         syslog (LOG_WARNING, "bdump_send: can't set up file pointers: %s",
494                 error_message(retval));
495         cleanup(server);
496         return;
497     }
498     retval = bdump_send_loop(server);
499     if (retval != ZERR_NONE) {
500         syslog(LOG_WARNING, "bdump_send: bdump_send_loop failed: %s",
501                error_message(retval));
502         cleanup(server);
503         return;
504     }
505     retval = bdump_recv_loop(server);
506     if (retval != ZERR_NONE) {
507         syslog(LOG_WARNING, "bdump_send: bdump_recv_loop failed: %s",
508                error_message(retval));
509         cleanup(server);
510         return;
511     }
512 #if 1
513     zdbug((LOG_DEBUG, "bdump_send: finished"));
514 #endif
515     if (server != limbo_server) {
516         /* set this guy to be up, and schedule a hello */
517         server->state = SERV_UP;
518         timer_reset(server->timer);
519         server->timer = timer_set_rel(0L, server_timo, server);
520     }
521 #if 0
522     zdbug((LOG_DEBUG,"cleanup sbd"));
523 #endif
524     shutdown_file_pointers();
525
526 #ifdef _POSIX_VERSION
527     action.sa_handler = SIG_DFL;
528     sigaction(SIGPIPE, &action, NULL);
529 #else
530     signal(SIGPIPE, SIG_DFL);
531 #endif
532     bdumping = 0;
533     server->dumping = 0;
534     /* Now that we are finished dumping, send all the queued packets */
535     server_send_queue(server);
536     return;
537 }
538
539 /*ARGSUSED*/
540 static void
541 bdump_get_v12 (ZNotice_t *notice,
542                int auth,
543                struct sockaddr_in *who,
544                Server *server)
545 {
546     struct sockaddr_in from;
547     Code_t retval;
548     int on = 1;
549 #ifdef _POSIX_VERSION
550     struct sigaction action;
551 #endif
552 #if defined(HAVE_KRB4) || defined(HAVE_KRB5)
553 #ifdef HAVE_KRB5
554     krb5_creds creds;
555     krb5_creds *credsp;
556     krb5_principal principal;
557     krb5_data data;
558     krb5_ap_rep_enc_part *rep;
559 #endif
560 #ifdef HAVE_KRB4
561     KTEXT_ST ticket;
562     AUTH_DAT kdata;
563 #endif
564 #else  /* !HAVE_KRB4 && !HAVE_KRB5 */
565     int reserved_port = IPPORT_RESERVED - 1;
566 #endif /* !HAVE_KRB4 && !HAVE_KRB5 */
567     
568     bdumping = 1;
569     server->dumping = 1;
570  
571 #ifdef _POSIX_VERSION
572     action.sa_flags = 0;
573     sigemptyset(&action.sa_mask);
574     action.sa_handler = SIG_IGN;
575     sigaction(SIGPIPE, &action, NULL);
576 #else
577     signal(SIGPIPE, SIG_IGN);   /* so we can detect problems */
578 #endif /* _POSIX_VRESION */
579  
580     if (bdump_socket >= 0) {
581         /* We cannot go get a brain dump when someone may
582            potentially be connecting to us (if that other
583            server is the server to whom we are connecting,
584            we will deadlock. so we shut down the listening
585            socket and the timer. */
586         FD_CLR(bdump_socket, &interesting);
587         close(bdump_socket);
588         nfds = srv_socket+1;
589         bdump_socket = -1;
590         timer_reset(bdump_timer);
591     }
592
593     retval = extract_sin(notice, &from);
594     if (retval != ZERR_NONE) {
595         syslog(LOG_ERR, "bdump_get: sin: %s", error_message(retval));
596 #ifdef _POSIX_VERSION
597         action.sa_handler = SIG_DFL;
598         sigaction(SIGPIPE, &action, NULL);
599 #else
600         signal(SIGPIPE, SIG_DFL);
601 #endif
602         bdumping = 0;
603         server->dumping = 0;
604         return;
605     }
606 #if !defined(HAVE_KRB4) && !defined(HAVE_KRB5)
607     if (ntohs(from.sin_port) > IPPORT_RESERVED ||
608         ntohs(from.sin_port) < IPPORT_RESERVED / 2) {
609         syslog(LOG_ERR, "bdump_get: port not reserved: %d",
610                ntohs(from.sin_port));
611         cleanup(server);
612         return;
613     }
614     live_socket = rresvport(&reserved_port);
615 #else  /* !HAVE_KRB4 && !HAVE_KRB5 */
616     live_socket = socket(AF_INET, SOCK_STREAM, 0);
617 #endif /* !HAVE_KRB4 && !HAVE_KRB5 */
618     if (live_socket < 0) {
619         syslog(LOG_ERR, "bdump_get: socket: %m");
620         cleanup(server);
621         return;
622     }
623     if (connect(live_socket, (struct sockaddr *) &from, sizeof(from))) {
624         syslog(LOG_ERR, "bdump_get: connect: %m");
625         cleanup(server);
626         return;
627     }
628     if (setsockopt(live_socket, SOL_SOCKET, SO_KEEPALIVE, (char *)&on,
629                    sizeof(on)) < 0)
630         syslog(LOG_WARNING, "bdump_get: setsockopt (SO_KEEPALIVE): %m");
631 #if 1
632     zdbug((LOG_DEBUG, "bdump_get: connected"));
633 #endif
634  
635     /* Now begin the brain dump. */
636 #if defined(HAVE_KRB4) || defined(HAVE_KRB5)
637     if (get_tgt()) {
638         syslog(LOG_ERR, "bdump_get: get_tgt failed"); 
639         cleanup(server);
640         return;
641     }
642     switch(bdump_auth_proto) {
643 #ifdef HAVE_KRB5
644     case 5: /* "client" side */
645         memset((char *)&creds, 0, sizeof(creds));
646
647         retval = krb5_build_principal(Z_krb5_ctx, &principal, 
648                                       strlen(ZGetRealm()),
649                                       ZGetRealm(),
650                                       SERVER_KRB5_SERVICE, SERVER_INSTANCE,
651                                       0); 
652         if (retval) {
653             syslog(LOG_ERR, "bdump_get: krb5_build_principal: %s", error_message(retval));
654             cleanup(server);
655             return;
656         }
657
658         retval = krb5_copy_principal(Z_krb5_ctx, principal, &creds.server);
659         if (retval) {
660             syslog(LOG_ERR, "bdump_get: krb5_copy_principal (server): %s", error_message(retval));
661             krb5_free_principal(Z_krb5_ctx, principal);
662             cleanup(server);
663             return;
664         }
665         
666         retval = krb5_copy_principal(Z_krb5_ctx, principal, &creds.client);
667         krb5_free_principal(Z_krb5_ctx, principal);
668         if (retval) {
669             syslog(LOG_ERR, "bdump_get: krb5_copy_principal (client): %s", error_message(retval));
670             krb5_free_cred_contents(Z_krb5_ctx, &creds);
671             cleanup(server);
672             return;
673         }
674
675         retval = krb5_get_credentials(Z_krb5_ctx, 0, Z_krb5_ccache,
676                                       &creds, &credsp);
677         krb5_free_cred_contents(Z_krb5_ctx, &creds);
678         if (retval) {
679             syslog(LOG_ERR, "bdump_get: krb5_get_credentials: %s", error_message(retval));
680             cleanup(server);
681             return;
682         }
683
684         retval = krb5_auth_con_init(Z_krb5_ctx, &bdump_ac);
685         if (retval) {
686             syslog(LOG_ERR, "bdump_get: krb5_auth_con_init: %s", error_message(retval));
687             krb5_free_creds(Z_krb5_ctx, credsp);
688             cleanup(server);
689             return;
690         }
691
692         retval = krb5_auth_con_setflags(Z_krb5_ctx, bdump_ac, KRB5_AUTH_CONTEXT_DO_SEQUENCE);
693         if (retval) {
694             syslog(LOG_ERR, "bdump_get: krb5_auth_con_setflags: %s", error_message(retval));
695             krb5_free_creds(Z_krb5_ctx, credsp);
696             cleanup(server);
697             return;
698         }
699
700         retval = krb5_auth_con_genaddrs(Z_krb5_ctx, bdump_ac, live_socket,
701                 KRB5_AUTH_CONTEXT_GENERATE_LOCAL_FULL_ADDR|KRB5_AUTH_CONTEXT_GENERATE_REMOTE_FULL_ADDR);
702         if (retval) {
703             syslog(LOG_ERR, "bdump_get: krb5_auth_con_genaddrs: %s", error_message(retval));
704             krb5_free_creds(Z_krb5_ctx, credsp);
705             cleanup(server);
706             return;
707         }
708
709         memset((char *)&data, 0, sizeof(krb5_data));
710         retval = krb5_mk_req_extended(Z_krb5_ctx, &bdump_ac, AP_OPTS_MUTUAL_REQUIRED|AP_OPTS_USE_SUBKEY,
711                                  NULL, credsp, &data);
712         if (retval) {
713             syslog(LOG_ERR, "bdump_get: krb5_mk_req_ext: %s", error_message(retval));
714             krb5_free_creds(Z_krb5_ctx, credsp);
715             cleanup(server);
716             return;
717         }
718         retval = SendKrb5Data(live_socket, &data);
719         krb5_free_creds(Z_krb5_ctx, credsp);
720         if (retval) {
721              syslog(LOG_ERR, "bdump_get: cannot send authenticator: %s",
722                     error_message(retval));
723              krb5_free_data_contents(Z_krb5_ctx, &data);
724              cleanup(server);
725              return;
726         }    
727         krb5_free_data_contents(Z_krb5_ctx, &data);
728         memset((char *)&data, 0, sizeof(krb5_data));
729         retval = GetKrb5Data(live_socket, &data);
730         if (retval) {
731              syslog(LOG_ERR, "bdump_get: cannot get auth response: %s",
732                     error_message(retval));
733              cleanup(server);
734              return;
735         }    
736         retval = krb5_rd_rep(Z_krb5_ctx, bdump_ac, &data, &rep);
737         free(data.data);
738         memset((char *)&data, 0, sizeof(krb5_data));
739         if (retval) {
740              syslog(LOG_ERR, "bdump_get: mutual authentication failed: %s",
741                     error_message(retval));
742              cleanup(server);
743              return;
744         }    
745         break;
746 #endif
747 #ifdef HAVE_KRB4
748     case 4:
749         /* send an authenticator */
750         retval = SendKerberosData(live_socket, &ticket, SERVER_SERVICE,
751                                   SERVER_INSTANCE);
752         if (retval != 0) {
753             syslog(LOG_ERR,"bdump_get: %s", error_message(retval));
754             cleanup(server);
755             return;
756         }
757         zdbug((LOG_DEBUG, "bdump_get: SendKerberosData ok"));
758         
759         /* get his authenticator */
760         retval = GetKerberosData(live_socket, from.sin_addr, &kdata,
761                                  SERVER_SERVICE, srvtab_file);
762         if (retval != KSUCCESS) {
763             syslog(LOG_ERR, "bdump_get getkdata: %s",krb_get_err_text(retval));
764             cleanup(server);
765             return;
766         }
767
768         if (strcmp(kdata.pname, SERVER_SERVICE) ||
769             strcmp(kdata.pinst, SERVER_INSTANCE) ||
770             strcmp(kdata.prealm, ZGetRealm())) {
771             syslog(LOG_ERR, "bdump_get: peer not zephyr in lrealm: %s.%s@%s",
772                    kdata.pname, kdata.pinst,kdata.prealm);
773             cleanup(server);
774             return;
775         }
776         break;
777 #endif /* HAVE_KRB4 */
778     }
779 #endif /* defined(HAVE_KRB4) || defined(HAVE_KRB5) */   
780     retval = setup_file_pointers();
781     if (retval != 0) {
782         syslog(LOG_WARNING, "bdump_get: can't set up file pointers: %s",
783                error_message (retval));
784         cleanup(server);
785         return;
786     }
787     retval = bdump_recv_loop(server);
788     if (retval != ZERR_NONE) {
789         syslog(LOG_WARNING, "bdump_get: bdump_recv_loop failed: %s",
790                error_message(retval));
791         cleanup(server);
792         return;
793     }
794     zdbug((LOG_DEBUG,"bdump_get: gbdl ok"));
795     retval = bdump_send_loop(server);
796     if (retval != ZERR_NONE) {
797         syslog(LOG_WARNING, "bdump_send_loop failed: %s",
798                error_message(retval));
799         cleanup(server);
800         return;
801     }
802 #if 1
803     zdbug((LOG_DEBUG, "bdump_get: gbd finished"));
804 #endif
805     /* set this guy to be up, and schedule a hello */
806     server->state = SERV_UP;
807     timer_reset(server->timer);
808     server->timer = timer_set_rel(0L, server_timo, server);
809
810 #if 1
811     zdbug((LOG_DEBUG,"cleanup gbd"));
812 #endif
813     shutdown_file_pointers();
814 #ifdef _POSIX_VERSION
815     action.sa_handler = SIG_DFL;
816     sigaction(SIGPIPE, &action, NULL);
817 #else
818     signal(SIGPIPE, SIG_DFL);
819 #endif
820     bdumping = 0;
821     server->dumping = 0;
822     /* Now that we are finished dumping, send all the queued packets */
823     server_send_queue(server);
824
825     return;
826 }
827
828 void
829 bdump_get(ZNotice_t *notice,
830           int auth,
831           struct sockaddr_in *who,
832           Server *server)
833 {
834     void (*proc)(ZNotice_t *, int, struct sockaddr_in *, Server *);
835
836     proc = NULL;
837
838 #if 1
839     if (zdebug) {
840         syslog(LOG_DEBUG, "bdump_get: bdump v%s avail %s",
841                notice->z_class_inst, inet_ntoa(who->sin_addr));
842     }
843 #endif
844     if (strcmp (notice->z_class_inst, "1.2") == 0)
845         proc = bdump_get_v12;
846
847     if (proc) {
848         (*proc)(notice, auth, who, server);
849     } else {
850         syslog(LOG_WARNING,
851                "bdump_get: Incompatible bdump version '%s' from %s",
852                notice->z_class_inst,
853                inet_ntoa(who->sin_addr));
854     }
855 }
856
857 /*
858  * Send a list off as the specified notice
859  */
860
861 Code_t
862 bdump_send_list_tcp(ZNotice_Kind_t kind,
863                     struct sockaddr_in *addr,
864                     char *class_name,
865                     char *inst,
866                     char *opcode,
867                     char *sender,
868                     char *recip,
869                     char **lyst,
870                     int num)
871 {
872     ZNotice_t notice;
873     char *pack, addrbuf[100];
874     int packlen, count;
875     Code_t retval;
876     u_short length;
877
878     memset (&notice, 0, sizeof(notice));
879  
880     retval = ZMakeAscii(addrbuf, sizeof(addrbuf),
881                         (unsigned char *) &addr->sin_addr,
882                         sizeof(struct in_addr));
883     if (retval != ZERR_NONE)
884         return retval;
885     notice.z_kind = kind;
886  
887     notice.z_port = addr->sin_port;
888     notice.z_class = class_name;
889     notice.z_class_inst = inst;
890     notice.z_opcode = opcode;
891     notice.z_sender = sender;
892     notice.z_recipient = recip;
893     notice.z_default_format = "";
894     notice.z_num_other_fields = 1;
895     notice.z_other_fields[0] = addrbuf;
896  
897     retval = ZFormatNoticeList(&notice, lyst, num, &pack, &packlen, ZNOAUTH);
898     if (retval != ZERR_NONE)
899         return retval;
900
901 #ifdef HAVE_KRB5
902     if (bdump_ac) {
903         krb5_data indata, outmsg;
904         indata.length=packlen;
905         indata.data=pack;
906         memset(&outmsg, 0, sizeof(krb5_data));
907         retval = krb5_mk_priv(Z_krb5_ctx, bdump_ac, &indata, &outmsg, NULL);
908         if (retval != ZERR_NONE)
909             return retval;
910         if (outmsg.length > Z_MAXPKTLEN) {
911             syslog(LOG_ERR, "bsl: encrypted packet is too large");
912             return ZERR_PKTLEN;
913         }
914         packlen = outmsg.length;
915         free(pack);
916         pack=malloc(packlen);
917         if (!pack)
918             return ENOMEM;
919         memcpy(pack, outmsg.data, packlen);
920         krb5_free_data_contents(Z_krb5_ctx, &outmsg);
921     }
922 #endif
923
924     length = htons((u_short) packlen);
925  
926     count = net_write(output, (char *) &length, sizeof(length));
927     if (count != sizeof(length)) {
928         if (count < 0) {
929             free(pack);
930             return(errno);
931         } else {
932             syslog(LOG_WARNING, "slt (length) xmit: %d vs %d",
933                    sizeof(length), count);
934             free(pack);
935             return(ZSRV_PKSHORT);
936         }
937     }
938  
939     count = net_write(output, pack, packlen);
940     if (count != packlen) {
941         if (count < 0) {
942             free(pack);
943             return(errno);
944         } else {
945             syslog(LOG_WARNING, "slt (packet) xmit: %d vs %d",
946                    packlen, count);
947             free(pack);
948             return(ZSRV_PKSHORT);
949         }
950     }
951     free(pack);
952     return(ZERR_NONE);
953 }
954
955 static void
956 shutdown_file_pointers(void)
957 {
958     if (input) {
959         fclose(input);
960         input = 0;
961     }
962     if (output) {
963         fclose(output);
964         output = 0;
965     }
966     if (live_socket >= 0) {
967         close(live_socket);
968         live_socket = -1;
969 #ifdef HAVE_KRB5
970         if (bdump_ac)
971                 krb5_auth_con_free(Z_krb5_ctx, bdump_ac);
972         bdump_ac = NULL;
973 #endif
974     }
975 }
976
977 static void
978 cleanup(Server *server)
979 {
980 #ifdef _POSIX_VERSION
981     struct sigaction action;
982 #endif
983
984 #if 1
985     zdbug((LOG_DEBUG, "bdump cleanup"));
986 #endif
987     if (server != limbo_server) {
988         server->state = SERV_DEAD;
989         timer_reset(server->timer);
990         server->timer = timer_set_rel(0L, server_timo, server);
991     }
992     shutdown_file_pointers ();
993 #ifdef _POSIX_VERSION
994     action.sa_flags = 0;
995     sigemptyset(&action.sa_mask);
996     action.sa_handler = SIG_DFL;
997     sigaction(SIGPIPE,&action, NULL);
998 #else
999     signal(SIGPIPE, SIG_DFL);
1000 #endif /* _POSIX_VERSION */
1001     bdumping = 0;
1002     server->dumping = 0;
1003 }
1004
1005 #ifdef HAVE_KRB4
1006 int
1007 get_tgt(void)
1008 {
1009     /* MIT Kerberos 4 get_svc_in_tkt() requires instance to be writable and
1010      * at least INST_SZ bytes long. */
1011     static char buf[INST_SZ + 1] = SERVER_INSTANCE;
1012     int retval = 0;
1013     CREDENTIALS cred;
1014 #ifndef NOENCRYPTION
1015     Sched *s;
1016 #endif
1017     
1018     /* have they expired ? */
1019     if (ticket_time < NOW - tkt_lifetime(TKTLIFETIME) + (15L * 60L)) {
1020         /* +15 for leeway */
1021 #if 0
1022         zdbug((LOG_DEBUG,"get new tickets: %d %d %d", ticket_time, NOW,
1023                NOW - tkt_lifetime(TKTLIFETIME) + 15L));
1024 #endif
1025         dest_tkt();
1026
1027         retval = krb_get_svc_in_tkt(SERVER_SERVICE, buf, ZGetRealm(),
1028                                     "krbtgt", ZGetRealm(),
1029                                     TKTLIFETIME, srvtab_file);
1030         if (retval != KSUCCESS) {
1031             syslog(LOG_ERR,"get_tgt: krb_get_svc_in_tkt: %s",
1032                    krb_get_err_text(retval));
1033             ticket_time = 0;
1034             return(1);
1035         } else {
1036             ticket_time = NOW;
1037         }
1038
1039 #ifndef NOENCRYPTION
1040         retval = read_service_key(SERVER_SERVICE, SERVER_INSTANCE,
1041                                   ZGetRealm(), 0 /*kvno*/,
1042                                   srvtab_file, serv_key);
1043         if (retval != KSUCCESS) {
1044             syslog(LOG_ERR, "get_tgt: read_service_key: %s",
1045                    krb_get_err_text(retval));
1046             return 1;
1047         }
1048         des_key_sched(serv_key, serv_ksched.s);
1049 #endif /* !NOENCRYPTION */
1050     }
1051 #ifdef HAVE_KRB5        
1052     /* XXX */
1053     if (ticket5_time < NOW - tkt5_lifetime(TKT5LIFETIME) + (15L * 60L)) {
1054         krb5_keytab kt;
1055         krb5_get_init_creds_opt opt;
1056         krb5_creds cred;
1057         krb5_principal principal;
1058
1059         memset(&cred, 0, sizeof(cred));
1060
1061         retval = krb5_build_principal(Z_krb5_ctx, &principal, 
1062                                       strlen(ZGetRealm()),
1063                                       ZGetRealm(),
1064                                       SERVER_KRB5_SERVICE, SERVER_INSTANCE,
1065                                       0); 
1066         if (retval) {
1067           krb5_free_principal(Z_krb5_ctx, principal);
1068           return(1);
1069         }
1070
1071         krb5_get_init_creds_opt_init (&opt);
1072         krb5_get_init_creds_opt_set_tkt_life (&opt, TKT5LIFETIME);
1073
1074         retval = krb5_kt_resolve(Z_krb5_ctx, keytab_file, &kt);
1075         if (retval) return(1);
1076         
1077         retval = krb5_get_init_creds_keytab (Z_krb5_ctx,
1078                                              &cred,
1079                                              principal,
1080                                              kt,
1081                                              0,
1082                                              NULL,
1083                                              &opt);
1084         krb5_free_principal(Z_krb5_ctx, principal);
1085         krb5_kt_close(Z_krb5_ctx, kt);
1086         if (retval) return(1);
1087
1088         retval = krb5_cc_initialize (Z_krb5_ctx, Z_krb5_ccache, cred.client);
1089         if (retval) return(1);
1090     
1091         retval = krb5_cc_store_cred (Z_krb5_ctx, Z_krb5_ccache, &cred);
1092         if (retval) return(1);
1093
1094         ticket5_time = NOW;
1095
1096         krb5_free_cred_contents (Z_krb5_ctx, &cred);
1097     }
1098 #endif
1099     return(0);
1100 }
1101 #endif /* HAVE_KRB4 */
1102
1103 /*
1104  * The braindump offer wasn't taken, so we retract it.
1105  */
1106  
1107 /*ARGSUSED*/
1108 static void
1109 close_bdump(void *arg)
1110 {
1111     if (bdump_socket >= 0) {
1112         FD_CLR(bdump_socket, &interesting);
1113         close(bdump_socket);
1114         nfds = srv_socket + 1;
1115         bdump_socket = -1;
1116 #if 1
1117         zdbug((LOG_DEBUG, "bdump not used"));
1118 #endif
1119     } else {
1120 #if 1
1121         zdbug((LOG_DEBUG, "bdump not open"));
1122 #endif
1123     }
1124     return;
1125 }
1126  
1127 /*
1128  * Start receiving instruction notices from the brain dump socket
1129  */
1130  
1131 static Code_t
1132 bdump_recv_loop(Server *server)
1133 {
1134     ZNotice_t notice;
1135     ZPacket_t packet;
1136     int len;
1137     Code_t retval;
1138     Client *client = NULL;
1139     struct sockaddr_in who;
1140 #ifdef HAVE_KRB5
1141     char buf[512];
1142     int blen;
1143 #endif
1144 #if defined(HAVE_KRB4) || defined(HAVE_KRB5)    
1145     char *cp;
1146 #endif
1147 #ifdef HAVE_KRB4
1148     C_Block cblock;
1149 #endif /* HAVE_KRB4 */
1150     ZRealm *realm = NULL;
1151  
1152 #if 1
1153     zdbug((LOG_DEBUG, "bdump recv loop"));
1154 #endif
1155         
1156     /* do the inverse of bdump_send_loop, registering stuff on the fly */
1157     while (1) {
1158         if (packets_waiting()) {
1159             /* A non-braindump packet is waiting; handle it. */
1160             bdumping = 0;
1161             bdump_concurrent = 1;
1162             handle_packet();
1163             bdump_concurrent = 0;
1164             bdumping = 1;
1165         }
1166         len = sizeof(packet);
1167         retval = get_packet(packet, len, &len);
1168         if (retval != ZERR_NONE) {
1169             syslog(LOG_ERR, "brl get pkt: %s", error_message(retval));
1170             return retval;
1171         }
1172
1173 #if HAVE_KRB5
1174         if (bdump_ac) {
1175             krb5_data in, out;
1176             in.length = len;
1177             in.data = packet;
1178             memset(&out, 0, sizeof(krb5_data));
1179             retval = krb5_rd_priv(Z_krb5_ctx, bdump_ac, &in, &out, NULL);
1180             if (retval != ZERR_NONE) {
1181                 syslog(LOG_ERR, "brl krb5 rd priv: %s", error_message(retval));
1182                 return retval;
1183             }
1184             memcpy(packet, out.data, out.length);
1185             len = out.length;
1186             krb5_free_data_contents(Z_krb5_ctx, &out);
1187         }
1188 #endif
1189
1190         retval = ZParseNotice(packet, len, &notice);
1191         if (retval != ZERR_NONE) {
1192             syslog(LOG_ERR, "brl notice parse: %s", error_message(retval));
1193             return retval;
1194         }
1195 #if defined (DEBUG)
1196         if (zdebug) {
1197             syslog(LOG_DEBUG, "bdump:%s '%s' '%s' '%s' '%s' '%s'",
1198                     ZNoticeKinds[(int) notice.z_kind], notice.z_class,
1199                     notice.z_class_inst, notice.z_opcode, notice.z_sender,
1200                     notice.z_recipient);
1201         }
1202 #endif /* DEBUG */
1203         if (notice.z_num_other_fields >= 1) {
1204             retval = ZReadAscii(notice.z_other_fields[0],
1205                                 strlen(notice.z_other_fields[0]),
1206                                 (unsigned char *) &who.sin_addr,
1207                                 sizeof(struct in_addr));
1208             if (retval != ZERR_NONE) {
1209                 syslog(LOG_ERR, "brl zreadascii failed: %s",
1210                        error_message(retval));
1211                 return retval;
1212             }
1213         } else {
1214             who.sin_addr.s_addr = notice.z_sender_addr.s_addr;
1215         }
1216         who.sin_family = AF_INET;
1217         who.sin_port = notice.z_port;
1218
1219         if (strcmp(notice.z_opcode, ADMIN_DONE) == 0) {
1220             /* end of brain dump */
1221             return ZERR_NONE;
1222         } else if (strcmp(notice.z_opcode, ADMIN_NEWREALM) == 0) {
1223             /* get a realm from the message */
1224             realm = realm_get_realm_by_name(notice.z_message);
1225             if (!realm) {
1226                 syslog(LOG_ERR, "brl newrlm failed: no realm %s", 
1227                        notice.z_message);
1228             }
1229         } else if (strcmp(notice.z_class, LOGIN_CLASS) == 0) {
1230             /* 1 = tell it we are authentic */
1231             retval = ulogin_dispatch(&notice, 1, &who, server);
1232             if (retval != ZERR_NONE) {
1233                 syslog(LOG_ERR, "brl ul_disp failed: %s",
1234                        error_message(retval));
1235                 return retval;
1236             }
1237         } else if (strcmp(notice.z_opcode, ADMIN_NEWCLT) == 0) {
1238             /* a new client */
1239             notice.z_port = htons((u_short) atoi(notice.z_message));
1240             retval = client_register(&notice, &who.sin_addr, &client, 0);
1241             if (retval != ZERR_NONE) {
1242                 syslog(LOG_ERR,"brl failed: %s", error_message(retval));
1243                 return retval;
1244             }
1245 #ifdef HAVE_KRB5
1246             client->session_keyblock = NULL;
1247             if (*notice.z_class_inst) {
1248                 /* check out this session key I found */
1249                 cp = notice.z_message + strlen(notice.z_message) + 1;
1250                 if (*cp == '0') {
1251                     /* ****ing netascii; this is an encrypted DES keyblock
1252                        XXX this code should be conditionalized for server
1253                        transitions   */
1254                     retval = Z_krb5_init_keyblock(Z_krb5_ctx, ENCTYPE_DES_CBC_CRC,
1255                                                 sizeof(C_Block),
1256                                                 &client->session_keyblock);
1257                     if (retval) {
1258                         syslog(LOG_ERR, "brl failed to allocate DES keyblock: %s",
1259                                error_message(retval));
1260                         return retval;
1261                     }
1262                     retval = ZReadAscii(cp, strlen(cp), cblock, sizeof(C_Block));
1263                     if (retval != ZERR_NONE) {
1264                         syslog(LOG_ERR,"brl bad cblk read: %s (%s)",
1265                                error_message(retval), cp);
1266                     } else {
1267                         des_ecb_encrypt(cblock, Z_keydata(client->session_keyblock),
1268                                         serv_ksched.s, DES_DECRYPT);
1269                     }
1270                 } else if (*cp == 'Z') { /* Zcode! Long live the new flesh! */
1271                     retval = ZReadZcode(cp, buf, sizeof(buf), &blen);
1272                     if (retval != ZERR_NONE) {
1273                         syslog(LOG_ERR,"brl bad cblk read: %s (%s)",
1274                                error_message(retval), cp);
1275                     } else {
1276                         retval = Z_krb5_init_keyblock(Z_krb5_ctx,
1277                                                     ntohl(*(krb5_enctype *)&buf[0]),
1278                                                     ntohl(*(u_int32_t *)&buf[4]),
1279                                                     &client->session_keyblock);
1280                         if (retval) {
1281                             syslog(LOG_ERR, "brl failed to allocate keyblock: %s",
1282                                    error_message(retval));
1283                             return retval;
1284                         }
1285                         memcpy(Z_keydata(client->session_keyblock), &buf[8],
1286                                Z_keylen(client->session_keyblock));
1287                     }
1288                 }
1289             }
1290 #else
1291 #ifdef HAVE_KRB4
1292             memset(client->session_key, 0, sizeof(C_Block));
1293             if (*notice.z_class_inst) {
1294                 /* a C_Block is there */
1295                 cp = notice.z_message + strlen(notice.z_message) + 1;
1296                 retval = ZReadAscii(cp, strlen(cp), cblock, sizeof(C_Block));
1297                 if (retval != ZERR_NONE) {
1298                     syslog(LOG_ERR,"brl bad cblk read: %s (%s)",
1299                            error_message(retval), cp);
1300                 } else {
1301 #ifdef NOENCRYPTION
1302                     memcpy(cblock, client->session_key, sizeof(C_Block));
1303 #else
1304                     des_ecb_encrypt(cblock, client->session_key, serv_ksched.s,
1305                                     DES_DECRYPT);
1306 #endif
1307                 }
1308             }
1309 #endif /* HAVE_KRB4 */
1310 #endif
1311         } else if (strcmp(notice.z_opcode, CLIENT_SUBSCRIBE) == 0) { 
1312             /* a subscription packet */
1313             if (!client) {
1314                 syslog(LOG_ERR, "brl no client");
1315                 return ZSRV_NOCLT;
1316             }
1317             retval = subscr_subscribe(client, &notice, server);
1318             if (retval != ZERR_NONE) {
1319                 syslog(LOG_WARNING, "brl subscr failed: %s",
1320                        error_message(retval));
1321                 return retval;
1322             }
1323         } else if (strcmp(notice.z_opcode, REALM_SUBSCRIBE) == 0) {
1324             /* add a subscription for a realm */
1325             if (realm) {
1326                 retval = subscr_realm(realm, &notice);
1327                 if (retval != ZERR_NONE) {
1328                     syslog(LOG_WARNING, "brl subscr failed: %s",
1329                            error_message(retval));
1330                     return retval;
1331                 }
1332             } /* else 
1333                  /* Other side tried to send us subs for a realm we didn't
1334                     know about, and so we drop them silently */
1335         
1336         } else {
1337             syslog(LOG_ERR, "brl bad opcode %s",notice.z_opcode);
1338             return ZSRV_UNKNOWNOPCODE;
1339         }
1340     }
1341 }
1342
1343 /*
1344  * Send all the state to the peer.
1345  */
1346
1347 static Code_t
1348 bdump_send_loop(Server *server)
1349 {
1350     Code_t retval;
1351
1352 #if 1
1353     zdbug((LOG_DEBUG, "bdump send loop"));
1354 #endif
1355
1356     retval = uloc_send_locations();
1357     if (retval != ZERR_NONE)
1358         return retval;
1359     retval = client_send_clients();
1360     if (retval != ZERR_NONE)
1361         return retval;
1362     retval = realm_send_realms();
1363     if (retval != ZERR_NONE)
1364         return retval;
1365     return send_done();
1366 }
1367
1368 /*
1369  * Send a sync indicating end of this host
1370  */
1371
1372 static Code_t
1373 send_done(void)
1374 {
1375     Code_t retval;
1376  
1377 #if 1
1378     zdbug((LOG_DEBUG, "send_done"));
1379 #endif
1380     retval = send_normal_tcp(SERVACK, bdump_sin.sin_port, ZEPHYR_ADMIN_CLASS,
1381                              "", ADMIN_DONE, myname, "", NULL, 0);
1382     return retval;
1383 }
1384
1385
1386 /*
1387  * Send a list off as the specified notice
1388  */
1389
1390 static Code_t
1391 send_list(ZNotice_Kind_t kind,
1392           int port,
1393           char *class_name,
1394           char *inst,
1395           char *opcode,
1396           char *sender,
1397           char *recip,
1398           char **lyst,
1399           int num)
1400 {
1401     ZNotice_t notice;
1402     char *pack;
1403     int packlen;
1404     Code_t retval;
1405  
1406     memset (&notice, 0, sizeof(notice));
1407
1408     notice.z_kind = kind;
1409     notice.z_port = port;
1410     notice.z_class = class_name;
1411     notice.z_class_inst = inst;
1412     notice.z_opcode = opcode;
1413     notice.z_sender = sender;
1414     notice.z_recipient = recip;
1415     notice.z_default_format = "";
1416     notice.z_num_other_fields = 0;
1417         
1418     retval = ZFormatNoticeList(&notice, lyst, num, &pack, &packlen, ZNOAUTH);
1419     if (retval != ZERR_NONE) {
1420         syslog(LOG_WARNING, "sl format: %s", error_message(retval));
1421         return retval;
1422     }
1423         
1424     retval = ZSendPacket(pack, packlen, 0);
1425     if (retval != ZERR_NONE)
1426         syslog(LOG_WARNING, "sl xmit: %s", error_message(retval));
1427     free(pack);
1428     return retval;
1429 }
1430
1431 /*
1432  * Send a message off as the specified notice, via TCP
1433  */
1434
1435 static Code_t
1436 send_normal_tcp(ZNotice_Kind_t kind,
1437                 int port,
1438                 char *class_name,
1439                 char *inst,
1440                 char *opcode,
1441                 char *sender,
1442                 char *recip,
1443                 char *message,
1444                 int len)
1445 {
1446     ZNotice_t notice;
1447     char *pack;
1448     int packlen, count;
1449     Code_t retval;
1450     u_short length;
1451  
1452     memset (&notice, 0, sizeof(notice));
1453
1454     notice.z_kind = kind;
1455     notice.z_port = port;
1456     notice.z_class = class_name;
1457     notice.z_class_inst = inst;
1458     notice.z_opcode = opcode;
1459     notice.z_sender = sender;
1460     notice.z_recipient = recip;
1461     notice.z_default_format = "";
1462     notice.z_message = message;
1463     notice.z_message_len = len;
1464     notice.z_num_other_fields = 0;
1465  
1466     retval = ZFormatNotice(&notice, &pack, &packlen, ZNOAUTH);
1467     if (retval != ZERR_NONE) {
1468         syslog(LOG_WARNING, "sn format: %s", error_message(retval));
1469         return retval;
1470     }
1471  
1472 #ifdef HAVE_KRB5
1473     if (bdump_ac) {
1474         krb5_data indata, outmsg;
1475         indata.length=packlen;
1476         indata.data=pack;
1477         memset(&outmsg, 0, sizeof(krb5_data));
1478         retval = krb5_mk_priv(Z_krb5_ctx, bdump_ac, &indata, &outmsg, NULL);
1479         if (retval != ZERR_NONE)
1480             return retval;
1481         if (outmsg.length > Z_MAXPKTLEN) {
1482             syslog(LOG_ERR, "sn: encrypted packet is too large");
1483             return ZERR_PKTLEN;
1484         }
1485         packlen = outmsg.length;
1486         free(pack);
1487         pack=malloc(packlen);
1488         if (!pack)
1489             return ENOMEM;
1490         memcpy(pack, outmsg.data, packlen);
1491         krb5_free_data_contents(Z_krb5_ctx, &outmsg);
1492     }
1493 #endif
1494
1495     length = htons((u_short) packlen);
1496  
1497     count = net_write(output, (char *) &length, sizeof(length));
1498     if (count != sizeof(length)) {
1499         if (count < 0) {
1500             syslog(LOG_WARNING, "snt xmit/len: %m");
1501             free(pack);
1502             return errno;
1503         } else {
1504             syslog(LOG_WARNING, "snt xmit: %d vs %d",sizeof(length),count);
1505             free(pack);
1506             return ZSRV_LEN;
1507         }
1508     }
1509     count = net_write(output, pack, packlen);
1510     if (count != packlen) {
1511         if (count < 0) {
1512             syslog(LOG_WARNING, "snt xmit: %m");
1513             free(pack);
1514             return errno;
1515         } else {
1516             syslog(LOG_WARNING, "snt xmit: %d vs %d",packlen, count);
1517             free(pack);
1518             return ZSRV_LEN;
1519         }
1520     }
1521     free(pack);
1522     return ZERR_NONE;
1523 }
1524
1525 /*
1526  * get a packet from the TCP socket
1527  * return 0 if successful, error code else
1528  */
1529
1530 static Code_t
1531 get_packet(void *packet, int len, int *retlen)
1532 {
1533     u_short length;
1534     int result;
1535  
1536     result = net_read(input, (char *) &length, sizeof(u_short));
1537     if (result < sizeof(short)) {
1538         if (result < 0) {
1539             return errno;
1540         } else {
1541             syslog(LOG_ERR, "get_pkt len: %d vs %d (%m)", result,
1542                    sizeof(short));
1543             return ZSRV_LEN;
1544         }
1545     }
1546         
1547     length = ntohs(length);
1548     if (len < length)
1549         return ZSRV_BUFSHORT;
1550     result = net_read(input, packet, (int) length);
1551     if (result < length) {
1552         if (result < 0) {
1553             return errno;
1554         } else {
1555             syslog(LOG_ERR, "get_pkt: %d vs %d (%m)", result, length);
1556             return ZSRV_LEN;
1557         }
1558     }
1559     *retlen = length;
1560     return ZERR_NONE;
1561 }
1562
1563 static Code_t
1564 extract_sin(ZNotice_t *notice, struct sockaddr_in *target)
1565 {
1566     char *cp = notice->z_message;
1567     char *buf;
1568
1569     buf = cp;
1570     if (!notice->z_message_len || *buf == '\0') {
1571 #if 0
1572         zdbug((LOG_DEBUG,"no addr"));
1573 #endif
1574         return ZSRV_PKSHORT;
1575     }
1576     target->sin_addr.s_addr = inet_addr(cp);
1577  
1578     cp += (strlen(cp) + 1);     /* past the null */
1579     if ((cp >= notice->z_message + notice->z_message_len) || (*cp == '\0')) {
1580 #if 0
1581         zdbug((LOG_DEBUG, "no port"));
1582 #endif
1583         return(ZSRV_PKSHORT);
1584     }
1585     target->sin_port = htons((u_short) atoi(cp));
1586     target->sin_family = AF_INET;
1587     return ZERR_NONE;
1588 }
1589
1590 static int
1591 net_read(FILE *f, char *buf, int len)
1592 {
1593     int cc, len2 = 0;
1594  
1595     fflush (output);
1596     do {
1597         errno = 0;
1598         cc = fread(buf, 1, len, f);
1599         if (cc == 0)
1600           {
1601             if (feof(f))
1602               return len2;
1603             if (errno == 0)
1604               errno = EIO;
1605             return -1;
1606           }
1607         buf += cc;
1608         len2 += cc;
1609         len -= cc;
1610     } while (len > 0);
1611     return len2;
1612 }
1613
1614 static int
1615 net_write(FILE *f, char *buf, int len)
1616 {
1617     int cc;
1618     int wrlen = len;
1619     do {
1620         cc = fwrite (buf, 1, wrlen, f);
1621         if (cc == 0)
1622             return -1;
1623         buf += cc;
1624         wrlen -= cc;
1625     } while (wrlen > 0);
1626     return len;
1627 }
1628
1629 static int
1630 setup_file_pointers (void)
1631 {
1632     int fd;
1633
1634     input = fdopen (live_socket, "r");
1635     if (!input)
1636         return errno;
1637
1638     fd = dup (live_socket);
1639     if (fd < 0)
1640         return errno;
1641     output = fdopen (fd, "w");
1642     if (!output)
1643         return errno;
1644
1645     return 0;
1646 }