Beispiel #1
0
int
RAND_write_file(const char *filename)
{
    unsigned char buf[128];
    size_t len;
    int res = 0, fd;

    fd = open(filename, O_WRONLY | O_CREAT | O_BINARY, 0600);
    if (fd < 0)
	return 0;
    rk_cloexec(fd);

    len = 0;
    while(len < RAND_FILE_SIZE) {
	res = RAND_bytes(buf, sizeof(buf));
	if (res != 1)
	    break;
	if (write(fd, buf, sizeof(buf)) != sizeof(buf)) {
	    res = 0;
	    break;
	}
	len += sizeof(buf);
    }

    close(fd);

    return res;
}
Beispiel #2
0
static krb5_error_code
fcc_open(krb5_context context,
	 krb5_ccache id,
	 int *fd_ret,
	 int flags,
	 mode_t mode)
{
    krb5_boolean exclusive = ((flags | O_WRONLY) == flags ||
			      (flags | O_RDWR) == flags);
    krb5_error_code ret;
    const char *filename = FILENAME(id);
    int fd;
    fd = open(filename, flags, mode);
    if(fd < 0) {
	ret = errno;
	krb5_set_error_message(context, ret, N_("open(%s): %s", "file, error"),
			       filename, strerror(ret));
	return ret;
    }
    rk_cloexec(fd);

    if((ret = fcc_lock(context, id, fd, exclusive)) != 0) {
	close(fd);
	return ret;
    }
    *fd_ret = fd;
    return 0;
}
Beispiel #3
0
static int
dir_iter_start(hx509_context context,
	       hx509_certs certs, void *data, void **cursor)
{
    struct dircursor *d;

    *cursor = NULL;

    d = calloc(1, sizeof(*d));
    if (d == NULL) {
	hx509_clear_error_string(context);
	return ENOMEM;
    }

    d->dir = opendir(data);
    if (d->dir == NULL) {
	hx509_clear_error_string(context);
	free(d);
	return errno;
    }
    rk_cloexec(dirfd(d->dir));
    d->certs = NULL;
    d->iter = NULL;

    *cursor = d;
    return 0;
}
Beispiel #4
0
static int
connect_egd(const char *path)
{
    struct sockaddr_un addr;
    int fd;

    memset(&addr, 0, sizeof(addr));

    if (strlen(path) > sizeof(addr.sun_path))
        return -1;

    addr.sun_family = AF_UNIX;
    strlcpy(addr.sun_path, path, sizeof(addr.sun_path));

    fd = socket(AF_UNIX, SOCK_STREAM, 0);
    if (fd < 0)
        return -1;

    rk_cloexec(fd);

    if (connect(fd, (struct sockaddr *)&addr, sizeof(addr)) != 0) {
        close(fd);
        return -1;
    }

    return fd;
}
Beispiel #5
0
static krb5_error_code
fkt_start_seq_get_int(krb5_context context,
		      krb5_keytab id,
		      int flags,
		      int exclusive,
		      krb5_kt_cursor *c)
{
    int8_t pvno, tag;
    krb5_error_code ret;
    struct fkt_data *d = id->data;

    c->fd = open (d->filename, flags);
    if (c->fd < 0) {
	ret = errno;
	krb5_set_error_message(context, ret,
			       N_("keytab %s open failed: %s", ""),
			       d->filename, strerror(ret));
	return ret;
    }
    rk_cloexec(c->fd);
    ret = _krb5_xlock(context, c->fd, exclusive, d->filename);
    if (ret) {
	close(c->fd);
	return ret;
    }
    c->sp = krb5_storage_from_fd(c->fd);
    if (c->sp == NULL) {
	_krb5_xunlock(context, c->fd);
	close(c->fd);
	return krb5_enomem(context);
    }
    krb5_storage_set_eof_code(c->sp, KRB5_KT_END);
    ret = krb5_ret_int8(c->sp, &pvno);
    if(ret) {
	krb5_storage_free(c->sp);
	_krb5_xunlock(context, c->fd);
	close(c->fd);
	krb5_clear_error_message(context);
	return ret;
    }
    if(pvno != 5) {
	krb5_storage_free(c->sp);
	_krb5_xunlock(context, c->fd);
	close(c->fd);
	krb5_clear_error_message (context);
	return KRB5_KEYTAB_BADVNO;
    }
    ret = krb5_ret_int8(c->sp, &tag);
    if (ret) {
	krb5_storage_free(c->sp);
	_krb5_xunlock(context, c->fd);
	close(c->fd);
	krb5_clear_error_message(context);
	return ret;
    }
    id->version = tag;
    storage_set_flags(context, c->sp, id->version);
    return 0;
}
Beispiel #6
0
static int
seed_something(void)
{
#ifndef NO_RANDFILE
    char buf[1024], seedfile[256];

    /* If there is a seed file, load it. But such a file cannot be trusted,
       so use 0 for the entropy estimate */
    if (RAND_file_name(seedfile, sizeof(seedfile))) {
	int fd;
	fd = open(seedfile, O_RDONLY | O_BINARY | O_CLOEXEC);
	if (fd >= 0) {
	    ssize_t ret;
	    rk_cloexec(fd);
	    ret = read(fd, buf, sizeof(buf));
	    if (ret > 0)
		RAND_add(buf, ret, 0.0);
	    close(fd);
	} else
	    seedfile[0] = '\0';
    } else
	seedfile[0] = '\0';
#endif

    /* Calling RAND_status() will try to use /dev/urandom if it exists so
       we do not have to deal with it. */
    if (RAND_status() != 1) {
#if defined(HAVE_RAND_EGD)
	krb5_context context;
	const char *p;

#ifndef OPENSSL_NO_EGD
	/* Try using egd */
	if (!krb5_init_context(&context)) {
	    p = krb5_config_get_string(context, NULL, "libdefaults",
				       "egd_socket", NULL);
	    if (p != NULL)
		RAND_egd_bytes(p, ENTROPY_NEEDED);
	    krb5_free_context(context);
	}
#endif

#else
	/* TODO: Once a Windows CryptoAPI RAND method is defined, we
	   can use that and failover to another method. */
#endif
    }

    if (RAND_status() == 1)	{
#ifndef NO_RANDFILE
	/* Update the seed file */
	if (seedfile[0])
	    RAND_write_file(seedfile);
#endif

	return 0;
    } else
	return -1;
}
Beispiel #7
0
int
_hc_unix_device_fd(int flags, const char **fn)
{
    static const char *rnd_devices[] = {
	"/dev/urandom",
	"/dev/random",
	"/dev/srandom",
	"/dev/arandom",
	NULL
    };
    const char **p;

    for(p = rnd_devices; *p; p++) {
	int fd = open(*p, flags | O_NDELAY);
	if(fd >= 0) {
	    if (fn)
		*fn = *p;
	    rk_cloexec(fd);
	    return fd;
	}
    }
    return -1;
}
Beispiel #8
0
int
RAND_load_file(const char *filename, size_t size)
{
    unsigned char buf[128];
    size_t len;
    ssize_t slen;
    int fd;

    fd = open(filename, O_RDONLY | O_BINARY, 0600);
    if (fd < 0)
	return 0;
    rk_cloexec(fd);
    len = 0;
    while(len < size) {
	slen = read(fd, buf, sizeof(buf));
	if (slen <= 0)
	    break;
	RAND_seed(buf, slen);
	len += slen;
    }
    close(fd);

    return len ? 1 : 0;
}
Beispiel #9
0
krb5_error_code
_krb5_erase_file(krb5_context context, const char *filename)
{
    int fd;
    struct stat sb1, sb2;
    int ret;

    ret = lstat (filename, &sb1);
    if (ret < 0)
	return errno;

    fd = open(filename, O_RDWR | O_BINARY);
    if(fd < 0) {
	if(errno == ENOENT)
	    return 0;
	else
	    return errno;
    }
    rk_cloexec(fd);
    ret = _krb5_xlock(context, fd, 1, filename);
    if (ret) {
	close(fd);
	return ret;
    }
    if (unlink(filename) < 0) {
	_krb5_xunlock(context, fd);
        close (fd);
        return errno;
    }
    ret = fstat (fd, &sb2);
    if (ret < 0) {
	_krb5_xunlock(context, fd);
	close (fd);
	return errno;
    }

    /* check if someone was playing with symlinks */

    if (sb1.st_dev != sb2.st_dev || sb1.st_ino != sb2.st_ino) {
	_krb5_xunlock(context, fd);
	close (fd);
	return EPERM;
    }

    /* there are still hard links to this file */

    if (sb2.st_nlink != 0) {
	_krb5_xunlock(context, fd);
        close (fd);
        return 0;
    }

    ret = scrub_file (fd);
    if (ret) {
	_krb5_xunlock(context, fd);
	close(fd);
	return ret;
    }
    ret = _krb5_xunlock(context, fd);
    close (fd);
    return ret;
}
Beispiel #10
0
static krb5_error_code KRB5_CALLCONV
fkt_add_entry(krb5_context context,
	      krb5_keytab id,
	      krb5_keytab_entry *entry)
{
    int ret;
    int fd;
    krb5_storage *sp;
    struct fkt_data *d = id->data;
    krb5_data keytab;
    int32_t len;

    fd = open (d->filename, O_RDWR | O_BINARY | O_CLOEXEC);
    if (fd < 0) {
	fd = open (d->filename, O_RDWR | O_CREAT | O_EXCL | O_BINARY | O_CLOEXEC, 0600);
	if (fd < 0) {
	    ret = errno;
	    krb5_set_error_message(context, ret,
				   N_("open(%s): %s", ""), d->filename,
				   strerror(ret));
	    return ret;
	}
	rk_cloexec(fd);

	ret = _krb5_xlock(context, fd, 1, d->filename);
	if (ret) {
	    close(fd);
	    return ret;
	}
	sp = krb5_storage_from_fd(fd);
	krb5_storage_set_eof_code(sp, KRB5_KT_END);
	ret = fkt_setup_keytab(context, id, sp);
	if(ret) {
	    goto out;
	}
	storage_set_flags(context, sp, id->version);
    } else {
	int8_t pvno, tag;

	rk_cloexec(fd);

	ret = _krb5_xlock(context, fd, 1, d->filename);
	if (ret) {
	    close(fd);
	    return ret;
	}
	sp = krb5_storage_from_fd(fd);
	krb5_storage_set_eof_code(sp, KRB5_KT_END);
	ret = krb5_ret_int8(sp, &pvno);
	if(ret) {
	    /* we probably have a zero byte file, so try to set it up
               properly */
	    ret = fkt_setup_keytab(context, id, sp);
	    if(ret) {
		krb5_set_error_message(context, ret,
				       N_("%s: keytab is corrupted: %s", ""),
				       d->filename, strerror(ret));
		goto out;
	    }
	    storage_set_flags(context, sp, id->version);
	} else {
	    if(pvno != 5) {
		ret = KRB5_KEYTAB_BADVNO;
		krb5_set_error_message(context, ret,
				       N_("Bad version in keytab %s", ""),
				       d->filename);
		goto out;
	    }
	    ret = krb5_ret_int8 (sp, &tag);
	    if (ret) {
		krb5_set_error_message(context, ret,
				       N_("failed reading tag from "
					  "keytab %s", ""),
				       d->filename);
		goto out;
	    }
	    id->version = tag;
	    storage_set_flags(context, sp, id->version);
	}
    }

    {
	krb5_storage *emem;
	emem = krb5_storage_emem();
	if(emem == NULL) {
	    ret = krb5_enomem(context);
	    goto out;
	}
	ret = krb5_kt_store_principal(context, emem, entry->principal);
	if(ret) {
	    krb5_set_error_message(context, ret,
				   N_("Failed storing principal "
				      "in keytab %s", ""),
				   d->filename);
	    krb5_storage_free(emem);
	    goto out;
	}
	ret = krb5_store_int32 (emem, entry->timestamp);
	if(ret) {
	    krb5_set_error_message(context, ret,
				   N_("Failed storing timpstamp "
				      "in keytab %s", ""),
				   d->filename);
	    krb5_storage_free(emem);
	    goto out;
	}
	ret = krb5_store_int8 (emem, entry->vno % 256);
	if(ret) {
	    krb5_set_error_message(context, ret,
				   N_("Failed storing kvno "
				      "in keytab %s", ""),
				   d->filename);
	    krb5_storage_free(emem);
	    goto out;
	}
	ret = krb5_kt_store_keyblock (context, d, emem, &entry->keyblock);
	if(ret) {
	    krb5_storage_free(emem);
	    goto out;
	}
	if ((d->flags & KRB5_KT_FL_JAVA) == 0) {
	    ret = krb5_store_int32 (emem, entry->vno);
	    if (ret) {
		krb5_set_error_message(context, ret,
				       N_("Failed storing extended kvno "
					  "in keytab %s", ""),
				       d->filename);
		krb5_storage_free(emem);
		goto out;
	    }
	    ret = krb5_store_uint32 (emem, entry->flags);
	    if (ret) {
		krb5_set_error_message(context, ret,
				       N_("Failed storing extended kvno "
					  "in keytab %s", ""),
				       d->filename);
		krb5_storage_free(emem);
		goto out;
	    }
	}

	ret = krb5_storage_to_data(emem, &keytab);
	krb5_storage_free(emem);
	if(ret) {
	    krb5_set_error_message(context, ret,
				   N_("Failed converting keytab entry "
				      "to memory block for keytab %s", ""),
				   d->filename);
	    goto out;
	}
    }

    while(1) {
	ret = krb5_ret_int32(sp, &len);
	if(ret == KRB5_KT_END) {
	    len = keytab.length;
	    break;
	}
	if(len < 0) {
	    len = -len;
	    if(len >= (int)keytab.length) {
		krb5_storage_seek(sp, -4, SEEK_CUR);
		break;
	    }
	}
	krb5_storage_seek(sp, len, SEEK_CUR);
    }
    ret = krb5_store_int32(sp, len);
    if(krb5_storage_write(sp, keytab.data, keytab.length) < 0) {
	ret = errno;
	krb5_set_error_message(context, ret,
			       N_("Failed writing keytab block "
				  "in keytab %s: %s", ""),
			       d->filename, strerror(ret));
    }
    memset(keytab.data, 0, keytab.length);
    krb5_data_free(&keytab);
  out:
    krb5_storage_free(sp);
    _krb5_xunlock(context, fd);
    close(fd);
    return ret;
}
Beispiel #11
0
krb5_error_code KRB5_LIB_FUNCTION
krb5_addlog_dest(krb5_context context, krb5_log_facility *f, const char *orig)
{
    krb5_error_code ret = 0;
    int min = 0, max = -1, n;
    char c;
    const char *p = orig;

    n = sscanf(p, "%d%c%d/", &min, &c, &max);
    if(n == 2){
	if(c == '/') {
	    if(min < 0){
		max = -min;
		min = 0;
	    }else{
		max = min;
	    }
	}
    }
    if(n){
	p = strchr(p, '/');
	if(p == NULL) {
	    krb5_set_error_message(context, HEIM_ERR_LOG_PARSE,
				   N_("failed to parse \"%s\"", ""), orig);
	    return HEIM_ERR_LOG_PARSE;
	}
	p++;
    }
    if(strcmp(p, "STDERR") == 0){
	ret = open_file(context, f, min, max, NULL, NULL, stderr, 1);
    }else if(strcmp(p, "CONSOLE") == 0){
	ret = open_file(context, f, min, max, "/dev/console", "w", NULL, 0);
    }else if(strncmp(p, "FILE", 4) == 0 && (p[4] == ':' || p[4] == '=')){
	char *fn;
	FILE *file = NULL;
	int keep_open = 0;
	fn = strdup(p + 5);
	if(fn == NULL) {
	    krb5_set_error_message(context, ENOMEM,
				   N_("malloc: out of memory", ""));
	    return ENOMEM;
	}
	if(p[4] == '='){
	    int i = open(fn, O_WRONLY | O_CREAT |
			 O_TRUNC | O_APPEND, 0666);
	    if(i < 0) {
		ret = errno;
		krb5_set_error_message(context, ret,
				       N_("open(%s) logile: %s", ""), fn,
				       strerror(ret));
		free(fn);
		return ret;
	    }
	    rk_cloexec(i);
	    file = fdopen(i, "a");
	    if(file == NULL){
		ret = errno;
		close(i);
		krb5_set_error_message(context, ret,
				       N_("fdopen(%s) logfile: %s", ""),
				       fn, strerror(ret));
		free(fn);
		return ret;
	    }
	    keep_open = 1;
	}
	ret = open_file(context, f, min, max, fn, "a", file, keep_open);
    }else if(strncmp(p, "DEVICE", 6) == 0 && (p[6] == ':' || p[6] == '=')){
	ret = open_file(context, f, min, max, strdup(p + 7), "w", NULL, 0);
    }else if(strncmp(p, "SYSLOG", 6) == 0 && (p[6] == '\0' || p[6] == ':')){
	char severity[128] = "";
	char facility[128] = "";
	p += 6;
	if(*p != '\0')
	    p++;
	if(strsep_copy(&p, ":", severity, sizeof(severity)) != -1)
	    strsep_copy(&p, ":", facility, sizeof(facility));
	if(*severity == '\0')
	    strlcpy(severity, "ERR", sizeof(severity));
 	if(*facility == '\0')
	    strlcpy(facility, "AUTH", sizeof(facility));
	ret = open_syslog(context, f, min, max, severity, facility);
    }else{
	ret = HEIM_ERR_LOG_PARSE; /* XXX */
	krb5_set_error_message (context, ret,
				N_("unknown log type: %s", ""), p);
    }
    return ret;
}
Beispiel #12
0
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL
krb5_sendto (krb5_context context,
	     const krb5_data *send_data,
	     krb5_krbhst_handle handle,
	     krb5_data *receive)
{
     krb5_error_code ret;
     krb5_socket_t fd;
     size_t i;

     krb5_data_zero(receive);

     for (i = 0; i < context->max_retries; ++i) {
	 krb5_krbhst_info *hi;

	 while (krb5_krbhst_next(context, handle, &hi) == 0) {
	     struct addrinfo *ai, *a;

	     _krb5_debug(context, 2,
			 "trying to communicate with host %s in realm %s",
			 hi->hostname, _krb5_krbhst_get_realm(handle));

	     if (context->send_to_kdc) {
		 struct send_to_kdc *s = context->send_to_kdc;

		 ret = (*s->func)(context, s->data, hi,
				  context->kdc_timeout, send_data, receive);
		 if (ret == 0 && receive->length != 0)
		     goto out;
		 continue;
	     }

	     ret = send_via_plugin(context, hi, context->kdc_timeout,
				   send_data, receive);
	     if (ret == 0 && receive->length != 0)
		 goto out;
	     else if (ret != KRB5_PLUGIN_NO_HANDLE)
		 continue;

	     if(hi->proto == KRB5_KRBHST_HTTP && context->http_proxy) {
		 if (send_via_proxy (context, hi, send_data, receive) == 0) {
		     ret = 0;
		     goto out;
		 }
		 continue;
	     }

	     ret = krb5_krbhst_get_addrinfo(context, hi, &ai);
	     if (ret)
		 continue;

	     for (a = ai; a != NULL; a = a->ai_next) {
		 fd = socket (a->ai_family, a->ai_socktype | SOCK_CLOEXEC, a->ai_protocol);
		 if (rk_IS_BAD_SOCKET(fd))
		     continue;
		 rk_cloexec(fd);
		 if (timed_connect (fd, a, context->kdc_timeout) < 0) {
		     rk_closesocket (fd);
		     continue;
		 }
		 switch (hi->proto) {
		 case KRB5_KRBHST_HTTP :
		     ret = send_and_recv_http(fd, context->kdc_timeout,
					      "", send_data, receive);
		     break;
		 case KRB5_KRBHST_TCP :
		     ret = send_and_recv_tcp (fd, context->kdc_timeout,
					      send_data, receive);
		     break;
		 case KRB5_KRBHST_UDP :
		     ret = send_and_recv_udp (fd, context->kdc_timeout,
					      send_data, receive);
		     break;
		 }
		 rk_closesocket (fd);
		 if(ret == 0 && receive->length != 0)
		     goto out;
	     }
	 }
	 krb5_krbhst_reset(context, handle);
     }
     krb5_clear_error_message (context);
     ret = KRB5_KDC_UNREACH;
out:
     _krb5_debug(context, 2,
		 "result of trying to talk to realm %s = %d",
		 _krb5_krbhst_get_realm(handle), ret);
     return ret;
}
Beispiel #13
0
static int
send_via_proxy (krb5_context context,
		const krb5_krbhst_info *hi,
		const krb5_data *send_data,
		krb5_data *receive)
{
    char *proxy2 = strdup(context->http_proxy);
    char *proxy  = proxy2;
    char *prefix = NULL;
    char *colon;
    struct addrinfo hints;
    struct addrinfo *ai, *a;
    int ret;
    krb5_socket_t s = rk_INVALID_SOCKET;
    char portstr[NI_MAXSERV];

    if (proxy == NULL)
	return ENOMEM;
    if (strncmp (proxy, "http://", 7) == 0)
	proxy += 7;

    colon = strchr(proxy, ':');
    if(colon != NULL)
	*colon++ = '\0';
    memset (&hints, 0, sizeof(hints));
    hints.ai_family   = PF_UNSPEC;
    hints.ai_socktype = SOCK_STREAM;
    snprintf (portstr, sizeof(portstr), "%d",
	      ntohs(init_port (colon, htons(80))));
    ret = getaddrinfo (proxy, portstr, &hints, &ai);
    free (proxy2);
    if (ret)
	return krb5_eai_to_heim_errno(ret, errno);

    for (a = ai; a != NULL; a = a->ai_next) {
	s = socket (a->ai_family, a->ai_socktype | SOCK_CLOEXEC, a->ai_protocol);
	if (s < 0)
	    continue;
	rk_cloexec(s);
	if (timed_connect (s, a, context->kdc_timeout) < 0) {
	    rk_closesocket (s);
	    continue;
	}
	break;
    }
    if (a == NULL) {
	freeaddrinfo (ai);
	return 1;
    }
    freeaddrinfo (ai);

    ret = asprintf(&prefix, "http://%s/", hi->hostname);
    if(ret < 0 || prefix == NULL) {
	close(s);
	return 1;
    }
    ret = send_and_recv_http(s, context->kdc_timeout,
			     prefix, send_data, receive);
    rk_closesocket (s);
    free(prefix);
    if(ret == 0 && receive->length != 0)
	return 0;
    return 1;
}
Beispiel #14
0
static int
fortuna_reseed(void)
{
    int entropy_p = 0;

    if (!init_done)
	abort();

#ifndef NO_RAND_UNIX_METHOD
    {
	unsigned char buf[INIT_BYTES];
	if ((*hc_rand_unix_method.bytes)(buf, sizeof(buf)) == 1) {
	    add_entropy(&main_state, buf, sizeof(buf));
	    entropy_p = 1;
	    memset(buf, 0, sizeof(buf));
	}
    }
#endif
#ifdef HAVE_ARC4RANDOM
    {
	uint32_t buf[INIT_BYTES / sizeof(uint32_t)];
	int i;

	for (i = 0; i < sizeof(buf)/sizeof(buf[0]); i++)
	    buf[i] = arc4random();
	add_entropy(&main_state, (void *)buf, sizeof(buf));
	entropy_p = 1;
    }
#endif
    /*
     * Fall back to gattering data from timer and secret files, this
     * is really the last resort.
     */
    if (!entropy_p) {
	/* to save stackspace */
	union {
	    unsigned char buf[INIT_BYTES];
	    unsigned char shad[1001];
	} u;
	int fd;

	/* add timer info */
	if ((*hc_rand_timer_method.bytes)(u.buf, sizeof(u.buf)) == 1)
	    add_entropy(&main_state, u.buf, sizeof(u.buf));
	/* add /etc/shadow */
	fd = open("/etc/shadow", O_RDONLY, 0);
	if (fd >= 0) {
	    ssize_t n;
	    rk_cloexec(fd);
	    /* add_entropy will hash the buf */
	    while ((n = read(fd, (char *)u.shad, sizeof(u.shad))) > 0)
		add_entropy(&main_state, u.shad, sizeof(u.shad));
	    close(fd);
	}

	memset(&u, 0, sizeof(u));

	entropy_p = 1; /* sure about this ? */
    }
    {
	pid_t pid = getpid();
	add_entropy(&main_state, (void *)&pid, sizeof(pid));
    }
    {
	struct timeval tv;
	gettimeofday(&tv, NULL);
	add_entropy(&main_state, (void *)&tv, sizeof(tv));
    }
#ifdef HAVE_GETUID
    {
	uid_t u = getuid();
	add_entropy(&main_state, (void *)&u, sizeof(u));
    }
#endif
    return entropy_p;
}
Beispiel #15
0
krb5_error_code KRB5_LIB_FUNCTION
krb5_sendto (krb5_context context,
	     const krb5_data *send_data,
	     krb5_krbhst_handle handle,	
	     krb5_data *receive)
{
     krb5_error_code ret;
     int fd;
     int i;

     krb5_data_zero(receive);

     for (i = 0; i < context->max_retries; ++i) {
	 krb5_krbhst_info *hi;

	 while (krb5_krbhst_next(context, handle, &hi) == 0) {
	     struct addrinfo *ai, *a;

	     if (context->send_to_kdc) {
		 struct send_to_kdc *s = context->send_to_kdc;

		 ret = (*s->func)(context, s->data,
				  hi, context->kdc_timeout, send_data, receive);
		 if (ret == 0 && receive->length != 0)
		     goto out;
		 continue;
	     }

	     ret = send_via_plugin(context, hi, context->kdc_timeout,
				   send_data, receive);
	     if (ret == 0 && receive->length != 0)
		 goto out;
	     else if (ret != KRB5_PLUGIN_NO_HANDLE)
		 continue;

	     if(hi->proto == KRB5_KRBHST_HTTP && context->http_proxy) {
		 if (send_via_proxy (context, hi, send_data, receive) == 0) {
		     ret = 0;
		     goto out;
		 }
		 continue;
	     }

	     ret = krb5_krbhst_get_addrinfo(context, hi, &ai);
	     if (ret)
		 continue;

	     for (a = ai; a != NULL; a = a->ai_next) {
		 fd = socket (a->ai_family, a->ai_socktype | SOCK_CLOEXEC, a->ai_protocol);
		 if (fd < 0)
		     continue;
		 rk_cloexec(fd);
		 if (connect (fd, a->ai_addr, a->ai_addrlen) < 0) {
		     close (fd);
		     continue;
		 }
		 switch (hi->proto) {
		 case KRB5_KRBHST_HTTP :
		     ret = send_and_recv_http(fd, context->kdc_timeout,
					      "", send_data, receive);
		     break;
		 case KRB5_KRBHST_TCP :
		     ret = send_and_recv_tcp (fd, context->kdc_timeout,
					      send_data, receive);
		     break;
		 case KRB5_KRBHST_UDP :
		     ret = send_and_recv_udp (fd, context->kdc_timeout,
					      send_data, receive);
		     break;
		 }
		 close (fd);
		 if(ret == 0 && receive->length != 0)
		     goto out;
	     }
	 }
	 krb5_krbhst_reset(context, handle);
     }
     krb5_clear_error_message (context);
     ret = KRB5_KDC_UNREACH;
out:
     return ret;
}