1/*	$NetBSD: ipropd_slave.c,v 1.1.1.1 2011/04/13 18:15:30 elric Exp $	*/
2
3/*
4 * Copyright (c) 1997 - 2008 Kungliga Tekniska Högskolan
5 * (Royal Institute of Technology, Stockholm, Sweden).
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 *
12 * 1. Redistributions of source code must retain the above copyright
13 *    notice, this list of conditions and the following disclaimer.
14 *
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in the
17 *    documentation and/or other materials provided with the distribution.
18 *
19 * 3. Neither the name of the Institute nor the names of its contributors
20 *    may be used to endorse or promote products derived from this software
21 *    without specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
34 */
35
36#include "iprop.h"
37
38__RCSID("$NetBSD: ipropd_slave.c,v 1.1.1.1 2011/04/13 18:15:30 elric Exp $");
39
40static const char *config_name = "ipropd-slave";
41
42static krb5_log_facility *log_facility;
43static char *server_time_lost = "5 min";
44static int time_before_lost;
45const char *slave_str = NULL;
46
47static int
48connect_to_master (krb5_context context, const char *master,
49		   const char *port_str)
50{
51    char port[NI_MAXSERV];
52    struct addrinfo *ai, *a;
53    struct addrinfo hints;
54    int error;
55    int s = -1;
56
57    memset (&hints, 0, sizeof(hints));
58    hints.ai_socktype = SOCK_STREAM;
59
60    if (port_str == NULL) {
61	snprintf(port, sizeof(port), "%u", IPROP_PORT);
62	port_str = port;
63    }
64
65    error = getaddrinfo (master, port_str, &hints, &ai);
66    if (error) {
67	krb5_warnx(context, "Failed to get address of to %s: %s",
68		   master, gai_strerror(error));
69	return -1;
70    }
71
72    for (a = ai; a != NULL; a = a->ai_next) {
73	char node[NI_MAXHOST];
74	error = getnameinfo(a->ai_addr, a->ai_addrlen,
75			    node, sizeof(node), NULL, 0, NI_NUMERICHOST);
76	if (error)
77	    strlcpy(node, "[unknown-addr]", sizeof(node));
78
79	s = socket (a->ai_family, a->ai_socktype, a->ai_protocol);
80	if (s < 0)
81	    continue;
82	if (connect (s, a->ai_addr, a->ai_addrlen) < 0) {
83	    krb5_warn(context, errno, "connection failed to %s[%s]",
84		      master, node);
85	    close (s);
86	    continue;
87	}
88	krb5_warnx(context, "connection successful "
89		   "to master: %s[%s]", master, node);
90	break;
91    }
92    freeaddrinfo (ai);
93
94    if (a == NULL)
95	return -1;
96
97    return s;
98}
99
100static void
101get_creds(krb5_context context, const char *keytab_str,
102	  krb5_ccache *cache, const char *serverhost)
103{
104    krb5_keytab keytab;
105    krb5_principal client;
106    krb5_error_code ret;
107    krb5_get_init_creds_opt *init_opts;
108    krb5_creds creds;
109    char *server;
110    char keytab_buf[256];
111
112    if (keytab_str == NULL) {
113	ret = krb5_kt_default_name (context, keytab_buf, sizeof(keytab_buf));
114	if (ret)
115	    krb5_err (context, 1, ret, "krb5_kt_default_name");
116	keytab_str = keytab_buf;
117    }
118
119    ret = krb5_kt_resolve(context, keytab_str, &keytab);
120    if(ret)
121	krb5_err(context, 1, ret, "%s", keytab_str);
122
123
124    ret = krb5_sname_to_principal (context, slave_str, IPROP_NAME,
125				   KRB5_NT_SRV_HST, &client);
126    if (ret) krb5_err(context, 1, ret, "krb5_sname_to_principal");
127
128    ret = krb5_get_init_creds_opt_alloc(context, &init_opts);
129    if (ret) krb5_err(context, 1, ret, "krb5_get_init_creds_opt_alloc");
130
131    asprintf (&server, "%s/%s", IPROP_NAME, serverhost);
132    if (server == NULL)
133	krb5_errx (context, 1, "malloc: no memory");
134
135    ret = krb5_get_init_creds_keytab(context, &creds, client, keytab,
136				     0, server, init_opts);
137    free (server);
138    krb5_get_init_creds_opt_free(context, init_opts);
139    if(ret) krb5_err(context, 1, ret, "krb5_get_init_creds");
140
141    ret = krb5_kt_close(context, keytab);
142    if(ret) krb5_err(context, 1, ret, "krb5_kt_close");
143
144    ret = krb5_cc_new_unique(context, krb5_cc_type_memory, NULL, cache);
145    if(ret) krb5_err(context, 1, ret, "krb5_cc_new_unique");
146
147    ret = krb5_cc_initialize(context, *cache, client);
148    if(ret) krb5_err(context, 1, ret, "krb5_cc_initialize");
149
150    ret = krb5_cc_store_cred(context, *cache, &creds);
151    if(ret) krb5_err(context, 1, ret, "krb5_cc_store_cred");
152
153    krb5_free_cred_contents(context, &creds);
154    krb5_free_principal(context, client);
155}
156
157static krb5_error_code
158ihave (krb5_context context, krb5_auth_context auth_context,
159       int fd, uint32_t version)
160{
161    int ret;
162    u_char buf[8];
163    krb5_storage *sp;
164    krb5_data data;
165
166    sp = krb5_storage_from_mem (buf, 8);
167    krb5_store_int32 (sp, I_HAVE);
168    krb5_store_int32 (sp, version);
169    krb5_storage_free (sp);
170    data.length = 8;
171    data.data   = buf;
172
173    ret = krb5_write_priv_message(context, auth_context, &fd, &data);
174    if (ret)
175	krb5_warn (context, ret, "krb5_write_message");
176    return ret;
177}
178
179static void
180receive_loop (krb5_context context,
181	      krb5_storage *sp,
182	      kadm5_server_context *server_context)
183{
184    int ret;
185    off_t left, right;
186    void *buf;
187    int32_t vers, vers2;
188    ssize_t sret;
189
190    /*
191     * Seek to the current version of the local database.
192     */
193    do {
194	int32_t len, timestamp, tmp;
195	enum kadm_ops op;
196
197	if(krb5_ret_int32 (sp, &vers) != 0)
198	    return;
199	krb5_ret_int32 (sp, &timestamp);
200	krb5_ret_int32 (sp, &tmp);
201	op = tmp;
202	krb5_ret_int32 (sp, &len);
203	if (vers <= server_context->log_context.version)
204	    krb5_storage_seek(sp, len + 8, SEEK_CUR);
205    } while(vers <= server_context->log_context.version);
206
207    /*
208     * Read up rest of the entires into the memory...
209     */
210    left  = krb5_storage_seek (sp, -16, SEEK_CUR);
211    right = krb5_storage_seek (sp, 0, SEEK_END);
212    buf = malloc (right - left);
213    if (buf == NULL && (right - left) != 0)
214	krb5_errx (context, 1, "malloc: no memory");
215
216    /*
217     * ...and then write them out to the on-disk log.
218     */
219    krb5_storage_seek (sp, left, SEEK_SET);
220    krb5_storage_read (sp, buf, right - left);
221    sret = write (server_context->log_context.log_fd, buf, right-left);
222    if (sret != right - left)
223	krb5_err(context, 1, errno, "Failed to write log to disk");
224    ret = fsync (server_context->log_context.log_fd);
225    if (ret)
226	krb5_err(context, 1, errno, "Failed to sync log to disk");
227    free (buf);
228
229    /*
230     * Go back to the startpoint and start to commit the entires to
231     * the database.
232     */
233    krb5_storage_seek (sp, left, SEEK_SET);
234
235    for(;;) {
236	int32_t len, len2, timestamp, tmp;
237	off_t cur, cur2;
238	enum kadm_ops op;
239
240	if(krb5_ret_int32 (sp, &vers) != 0)
241	    break;
242	ret = krb5_ret_int32 (sp, &timestamp);
243	if (ret) krb5_errx(context, 1, "entry %ld: too short", (long)vers);
244	ret = krb5_ret_int32 (sp, &tmp);
245	if (ret) krb5_errx(context, 1, "entry %ld: too short", (long)vers);
246	op = tmp;
247	ret = krb5_ret_int32 (sp, &len);
248	if (ret) krb5_errx(context, 1, "entry %ld: too short", (long)vers);
249	if (len < 0)
250	    krb5_errx(context, 1, "log is corrupted, "
251		      "negative length of entry version %ld: %ld",
252		      (long)vers, (long)len);
253	cur = krb5_storage_seek(sp, 0, SEEK_CUR);
254
255	krb5_warnx (context, "replaying entry %d", (int)vers);
256
257	ret = kadm5_log_replay (server_context,
258				op, vers, len, sp);
259	if (ret) {
260	    const char *s = krb5_get_error_message(server_context->context, ret);
261	    krb5_warnx (context,
262			"kadm5_log_replay: %ld. Lost entry entry, "
263			"Database out of sync ?: %s (%d)",
264			(long)vers, s ? s : "unknown error", ret);
265	    krb5_free_error_message(context, s);
266	}
267
268	{
269	    /*
270	     * Make sure the krb5_log_replay does the right thing wrt
271	     * reading out data from the sp.
272	     */
273	    cur2 = krb5_storage_seek(sp, 0, SEEK_CUR);
274	    if (cur + len != cur2)
275		krb5_errx(context, 1,
276			  "kadm5_log_reply version: %ld didn't read the whole entry",
277			  (long)vers);
278	}
279
280	if (krb5_ret_int32 (sp, &len2) != 0)
281	    krb5_errx(context, 1, "entry %ld: postamble too short", (long)vers);
282	if(krb5_ret_int32 (sp, &vers2) != 0)
283	    krb5_errx(context, 1, "entry %ld: postamble too short", (long)vers);
284
285	if (len != len2)
286	    krb5_errx(context, 1, "entry %ld: len != len2", (long)vers);
287	if (vers != vers2)
288	    krb5_errx(context, 1, "entry %ld: vers != vers2", (long)vers);
289    }
290
291    /*
292     * Update version
293     */
294
295    server_context->log_context.version = vers;
296}
297
298static void
299receive (krb5_context context,
300	 krb5_storage *sp,
301	 kadm5_server_context *server_context)
302{
303    int ret;
304
305    ret = server_context->db->hdb_open(context,
306				       server_context->db,
307				       O_RDWR | O_CREAT, 0600);
308    if (ret)
309	krb5_err (context, 1, ret, "db->open");
310
311    receive_loop (context, sp, server_context);
312
313    ret = server_context->db->hdb_close (context, server_context->db);
314    if (ret)
315	krb5_err (context, 1, ret, "db->close");
316}
317
318static void
319send_im_here (krb5_context context, int fd,
320	      krb5_auth_context auth_context)
321{
322    krb5_storage *sp;
323    krb5_data data;
324    int ret;
325
326    ret = krb5_data_alloc (&data, 4);
327    if (ret)
328	krb5_err (context, 1, ret, "send_im_here");
329
330    sp = krb5_storage_from_data (&data);
331    if (sp == NULL)
332	krb5_errx (context, 1, "krb5_storage_from_data");
333    krb5_store_int32(sp, I_AM_HERE);
334    krb5_storage_free(sp);
335
336    ret = krb5_write_priv_message(context, auth_context, &fd, &data);
337    krb5_data_free(&data);
338
339    if (ret)
340	krb5_err (context, 1, ret, "krb5_write_priv_message");
341}
342
343static krb5_error_code
344receive_everything (krb5_context context, int fd,
345		    kadm5_server_context *server_context,
346		    krb5_auth_context auth_context)
347{
348    int ret;
349    krb5_data data;
350    int32_t vno = 0;
351    int32_t opcode;
352    krb5_storage *sp;
353
354    char *dbname;
355    HDB *mydb;
356
357    krb5_warnx(context, "receive complete database");
358
359    asprintf(&dbname, "%s-NEW", server_context->db->hdb_name);
360    ret = hdb_create(context, &mydb, dbname);
361    if(ret)
362	krb5_err(context,1, ret, "hdb_create");
363    free(dbname);
364
365    ret = hdb_set_master_keyfile (context,
366				  mydb, server_context->config.stash_file);
367    if(ret)
368	krb5_err(context,1, ret, "hdb_set_master_keyfile");
369
370    /* I really want to use O_EXCL here, but given that I can't easily clean
371       up on error, I won't */
372    ret = mydb->hdb_open(context, mydb, O_RDWR | O_CREAT | O_TRUNC, 0600);
373    if (ret)
374	krb5_err (context, 1, ret, "db->open");
375
376    sp = NULL;
377    do {
378	ret = krb5_read_priv_message(context, auth_context, &fd, &data);
379
380	if (ret) {
381	    krb5_warn (context, ret, "krb5_read_priv_message");
382	    goto cleanup;
383	}
384
385	sp = krb5_storage_from_data (&data);
386	if (sp == NULL)
387	    krb5_errx (context, 1, "krb5_storage_from_data");
388	krb5_ret_int32 (sp, &opcode);
389	if (opcode == ONE_PRINC) {
390	    krb5_data fake_data;
391	    hdb_entry_ex entry;
392
393	    krb5_storage_free(sp);
394
395	    fake_data.data   = (char *)data.data + 4;
396	    fake_data.length = data.length - 4;
397
398	    memset(&entry, 0, sizeof(entry));
399
400	    ret = hdb_value2entry (context, &fake_data, &entry.entry);
401	    if (ret)
402		krb5_err (context, 1, ret, "hdb_value2entry");
403	    ret = mydb->hdb_store(server_context->context,
404				  mydb,
405				  0, &entry);
406	    if (ret)
407		krb5_err (context, 1, ret, "hdb_store");
408
409	    hdb_free_entry (context, &entry);
410	    krb5_data_free (&data);
411	} else if (opcode == NOW_YOU_HAVE)
412	    ;
413	else
414	    krb5_errx (context, 1, "strange opcode %d", opcode);
415    } while (opcode == ONE_PRINC);
416
417    if (opcode != NOW_YOU_HAVE)
418	krb5_errx (context, 1, "receive_everything: strange %d", opcode);
419
420    krb5_ret_int32 (sp, &vno);
421    krb5_storage_free(sp);
422
423    ret = kadm5_log_reinit (server_context);
424    if (ret)
425	krb5_err(context, 1, ret, "kadm5_log_reinit");
426
427    ret = kadm5_log_set_version (server_context, vno - 1);
428    if (ret)
429	krb5_err (context, 1, ret, "kadm5_log_set_version");
430
431    ret = kadm5_log_nop (server_context);
432    if (ret)
433	krb5_err (context, 1, ret, "kadm5_log_nop");
434
435    ret = mydb->hdb_rename (context, mydb, server_context->db->hdb_name);
436    if (ret)
437	krb5_err (context, 1, ret, "db->rename");
438
439 cleanup:
440    krb5_data_free (&data);
441
442    ret = mydb->hdb_close (context, mydb);
443    if (ret)
444	krb5_err (context, 1, ret, "db->close");
445
446    ret = mydb->hdb_destroy (context, mydb);
447    if (ret)
448	krb5_err (context, 1, ret, "db->destroy");
449
450    krb5_warnx(context, "receive complete database, version %ld", (long)vno);
451    return ret;
452}
453
454static char *config_file;
455static char *realm;
456static int version_flag;
457static int help_flag;
458static char *keytab_str;
459static char *port_str;
460#ifdef SUPPORT_DETACH
461static int detach_from_console = 0;
462#endif
463
464static struct getargs args[] = {
465    { "config-file", 'c', arg_string, &config_file },
466    { "realm", 'r', arg_string, &realm },
467    { "keytab", 'k', arg_string, &keytab_str,
468      "keytab to get authentication from", "kspec" },
469    { "time-lost", 0, arg_string, &server_time_lost,
470      "time before server is considered lost", "time" },
471    { "port", 0, arg_string, &port_str,
472      "port ipropd-slave will connect to", "port"},
473#ifdef SUPPORT_DETACH
474    { "detach", 0, arg_flag, &detach_from_console,
475      "detach from console" },
476#endif
477    { "hostname", 0, arg_string, rk_UNCONST(&slave_str),
478      "hostname of slave (if not same as hostname)", "hostname" },
479    { "version", 0, arg_flag, &version_flag },
480    { "help", 0, arg_flag, &help_flag }
481};
482
483static int num_args = sizeof(args) / sizeof(args[0]);
484
485static void
486usage(int status)
487{
488    arg_printusage(args, num_args, NULL, "master");
489    exit(status);
490}
491
492int
493main(int argc, char **argv)
494{
495    krb5_error_code ret;
496    krb5_context context;
497    krb5_auth_context auth_context;
498    void *kadm_handle;
499    kadm5_server_context *server_context;
500    kadm5_config_params conf;
501    int master_fd;
502    krb5_ccache ccache;
503    krb5_principal server;
504    char **files;
505    int optidx = 0;
506    time_t reconnect_min;
507    time_t backoff;
508    time_t reconnect_max;
509    time_t reconnect;
510    time_t before = 0;
511
512    const char *master;
513
514    setprogname(argv[0]);
515
516    if(getarg(args, num_args, argc, argv, &optidx))
517	usage(1);
518
519    if(help_flag)
520	usage(0);
521    if(version_flag) {
522	print_version(NULL);
523	exit(0);
524    }
525
526    ret = krb5_init_context(&context);
527    if (ret)
528	errx (1, "krb5_init_context failed: %d", ret);
529
530    setup_signal();
531
532    if (config_file == NULL) {
533	asprintf(&config_file, "%s/kdc.conf", hdb_db_dir(context));
534	if (config_file == NULL)
535	    errx(1, "out of memory");
536    }
537
538    ret = krb5_prepend_config_files_default(config_file, &files);
539    if (ret)
540	krb5_err(context, 1, ret, "getting configuration files");
541
542    ret = krb5_set_config_files(context, files);
543    krb5_free_config_files(files);
544    if (ret)
545	krb5_err(context, 1, ret, "reading configuration files");
546
547    argc -= optidx;
548    argv += optidx;
549
550    if (argc != 1)
551	usage(1);
552
553    master = argv[0];
554
555#ifdef SUPPORT_DETACH
556    if (detach_from_console)
557	daemon(0, 0);
558#endif
559    pidfile (NULL);
560    krb5_openlog (context, "ipropd-slave", &log_facility);
561    krb5_set_warn_dest(context, log_facility);
562
563    ret = krb5_kt_register(context, &hdb_kt_ops);
564    if(ret)
565	krb5_err(context, 1, ret, "krb5_kt_register");
566
567    time_before_lost = parse_time (server_time_lost,  "s");
568    if (time_before_lost < 0)
569	krb5_errx (context, 1, "couldn't parse time: %s", server_time_lost);
570
571    memset(&conf, 0, sizeof(conf));
572    if(realm) {
573	conf.mask |= KADM5_CONFIG_REALM;
574	conf.realm = realm;
575    }
576    ret = kadm5_init_with_password_ctx (context,
577					KADM5_ADMIN_SERVICE,
578					NULL,
579					KADM5_ADMIN_SERVICE,
580					&conf, 0, 0,
581					&kadm_handle);
582    if (ret)
583	krb5_err (context, 1, ret, "kadm5_init_with_password_ctx");
584
585    server_context = (kadm5_server_context *)kadm_handle;
586
587    ret = kadm5_log_init (server_context);
588    if (ret)
589	krb5_err (context, 1, ret, "kadm5_log_init");
590
591    get_creds(context, keytab_str, &ccache, master);
592
593    ret = krb5_sname_to_principal (context, master, IPROP_NAME,
594				   KRB5_NT_SRV_HST, &server);
595    if (ret)
596	krb5_err (context, 1, ret, "krb5_sname_to_principal");
597
598    auth_context = NULL;
599    master_fd = -1;
600
601    krb5_appdefault_time(context, config_name, NULL, "reconnect-min",
602			 10, &reconnect_min);
603    krb5_appdefault_time(context, config_name, NULL, "reconnect-max",
604			 300, &reconnect_max);
605    krb5_appdefault_time(context, config_name, NULL, "reconnect-backoff",
606			 10, &backoff);
607    reconnect = reconnect_min;
608
609    while (!exit_flag) {
610	time_t now, elapsed;
611	int connected = FALSE;
612
613	now = time(NULL);
614	elapsed = now - before;
615
616	if (elapsed < reconnect) {
617	    time_t left = reconnect - elapsed;
618	    krb5_warnx(context, "sleeping %d seconds before "
619		       "retrying to connect", (int)left);
620	    sleep(left);
621	}
622	before = now;
623
624	master_fd = connect_to_master (context, master, port_str);
625	if (master_fd < 0)
626	    goto retry;
627
628	reconnect = reconnect_min;
629
630	if (auth_context) {
631	    krb5_auth_con_free(context, auth_context);
632	    auth_context = NULL;
633	    krb5_cc_destroy(context, ccache);
634	    get_creds(context, keytab_str, &ccache, master);
635	}
636	ret = krb5_sendauth (context, &auth_context, &master_fd,
637			     IPROP_VERSION, NULL, server,
638			     AP_OPTS_MUTUAL_REQUIRED, NULL, NULL,
639			     ccache, NULL, NULL, NULL);
640	if (ret) {
641	    krb5_warn (context, ret, "krb5_sendauth");
642	    goto retry;
643	}
644
645	krb5_warnx(context, "ipropd-slave started at version: %ld",
646		   (long)server_context->log_context.version);
647
648	ret = ihave (context, auth_context, master_fd,
649		     server_context->log_context.version);
650	if (ret)
651	    goto retry;
652
653	connected = TRUE;
654
655	while (connected && !exit_flag) {
656	    krb5_data out;
657	    krb5_storage *sp;
658	    int32_t tmp;
659	    fd_set readset;
660	    struct timeval to;
661
662#ifndef NO_LIMIT_FD_SETSIZE
663	    if (master_fd >= FD_SETSIZE)
664		krb5_errx (context, 1, "fd too large");
665#endif
666
667	    FD_ZERO(&readset);
668	    FD_SET(master_fd, &readset);
669
670	    to.tv_sec = time_before_lost;
671	    to.tv_usec = 0;
672
673	    ret = select (master_fd + 1,
674			  &readset, NULL, NULL, &to);
675	    if (ret < 0) {
676		if (errno == EINTR)
677		    continue;
678		else
679		    krb5_err (context, 1, errno, "select");
680	    }
681	    if (ret == 0)
682		krb5_errx (context, 1, "server didn't send a message "
683			   "in %d seconds", time_before_lost);
684
685	    ret = krb5_read_priv_message(context, auth_context, &master_fd, &out);
686	    if (ret) {
687		krb5_warn (context, ret, "krb5_read_priv_message");
688		connected = FALSE;
689		continue;
690	    }
691
692	    sp = krb5_storage_from_mem (out.data, out.length);
693	    krb5_ret_int32 (sp, &tmp);
694	    switch (tmp) {
695	    case FOR_YOU :
696		receive (context, sp, server_context);
697		ret = ihave (context, auth_context, master_fd,
698			     server_context->log_context.version);
699		if (ret)
700		    connected = FALSE;
701		break;
702	    case TELL_YOU_EVERYTHING :
703		ret = receive_everything (context, master_fd, server_context,
704					  auth_context);
705		if (ret)
706		    connected = FALSE;
707		break;
708	    case ARE_YOU_THERE :
709		send_im_here (context, master_fd, auth_context);
710		break;
711	    case NOW_YOU_HAVE :
712	    case I_HAVE :
713	    case ONE_PRINC :
714	    case I_AM_HERE :
715	    default :
716		krb5_warnx (context, "Ignoring command %d", tmp);
717		break;
718	    }
719	    krb5_storage_free (sp);
720	    krb5_data_free (&out);
721
722	}
723    retry:
724	if (connected == FALSE)
725	    krb5_warnx (context, "disconnected for server");
726	if (exit_flag)
727	    krb5_warnx (context, "got an exit signal");
728
729	if (master_fd >= 0)
730	    close(master_fd);
731
732	reconnect += backoff;
733	if (reconnect > reconnect_max)
734	    reconnect = reconnect_max;
735    }
736
737    if (0);
738#ifndef NO_SIGXCPU
739    else if(exit_flag == SIGXCPU)
740	krb5_warnx(context, "%s CPU time limit exceeded", getprogname());
741#endif
742    else if(exit_flag == SIGINT || exit_flag == SIGTERM)
743	krb5_warnx(context, "%s terminated", getprogname());
744    else
745	krb5_warnx(context, "%s unexpected exit reason: %ld",
746		       getprogname(), (long)exit_flag);
747
748    return 0;
749}
750