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