예제 #1
0
/*
 * krb5 version of a security handle allocator.  Logically sets
 * up a network "connection".
 */
static void
krb5_connect(
    const char *hostname,
    char *	(*conf_fn)(char *, void *),
    void	(*fn)(void *, security_handle_t *, security_status_t),
    void *	arg,
    void *	datap)
{
    struct sec_handle *rh;
    int result;
    char *canonname;

    assert(fn != NULL);
    assert(hostname != NULL);

    auth_debug(1, "krb5: krb5_connect: %s\n", hostname);

    krb5_init();

    rh = alloc(sizeof(*rh));
    security_handleinit(&rh->sech, &krb5_security_driver);
    rh->hostname = NULL;
    rh->rs = NULL;
    rh->ev_timeout = NULL;
    rh->rc = NULL;

    result = resolve_hostname(hostname, 0, NULL, &canonname);
    if(result != 0) {
	dbprintf(_("resolve_hostname(%s): %s\n"), hostname, gai_strerror(result));
	security_seterror(&rh->sech, _("resolve_hostname(%s): %s\n"), hostname,
			  gai_strerror(result));
	(*fn)(arg, &rh->sech, S_ERROR);
	return;
    }
    if (canonname == NULL) {
	dbprintf(_("resolve_hostname(%s) did not return a canonical name\n"), hostname);
	security_seterror(&rh->sech,
	        _("resolve_hostname(%s) did not return a canonical name\n"), hostname);
	(*fn)(arg, &rh->sech, S_ERROR);
       return;
    }

    rh->hostname = canonname;        /* will be replaced */
    canonname = NULL; /* steal reference */
    rh->rs = tcpma_stream_client(rh, newhandle++);
    rh->rc->conf_fn = conf_fn;
    rh->rc->datap = datap;
    rh->rc->recv_security_ok = NULL;
    rh->rc->prefix_packet = NULL;

    if (rh->rs == NULL)
	goto error;

    amfree(rh->hostname);
    rh->hostname = stralloc(rh->rs->rc->hostname);

#ifdef AMANDA_KEYTAB
    keytab_name = AMANDA_KEYTAB;
#else
    if(conf_fn) {
        keytab_name = conf_fn("krb5keytab", datap);
    }
#endif
#ifdef AMANDA_PRINCIPAL
    principal_name = AMANDA_PRINCIPAL;
#else
    if(conf_fn) {
        principal_name = conf_fn("krb5principal", datap);
    }
#endif

    /*
     * We need to open a new connection.
     *
     * XXX need to eventually limit number of outgoing connections here.
     */
    if(rh->rc->read == -1) {
	if (runkrb5(rh) < 0)
	    goto error;
	rh->rc->refcnt++;
    }

    /*
     * The socket will be opened async so hosts that are down won't
     * block everything.  We need to register a write event
     * so we will know when the socket comes alive.
     *
     * Overload rh->rs->ev_read to provide a write event handle.
     * We also register a timeout.
     */
    rh->fn.connect = fn;
    rh->arg = arg;
    rh->rs->ev_read = event_register((event_id_t)(rh->rs->rc->write),
	EV_WRITEFD, sec_connect_callback, rh);
    rh->ev_timeout = event_register(CONNECT_TIMEOUT, EV_TIME,
	sec_connect_timeout, rh);

    amfree(canonname);
    return;

error:
    amfree(canonname);
    (*fn)(arg, &rh->sech, S_ERROR);
}
예제 #2
0
/*
 * ssh version of a security handle allocator.  Logically sets
 * up a network "connection".
 */
static void
ssh_connect(
    const char *	hostname,
    char *		(*conf_fn)(char *, void *),
    void		(*fn)(void *, security_handle_t *, security_status_t),
    void *		arg,
    void *		datap)
{
    int result;
    struct sec_handle *rh;
    char *amandad_path=NULL, *client_username=NULL, *ssh_keys=NULL;

    assert(fn != NULL);
    assert(hostname != NULL);

    auth_debug(1, "ssh_connect: %s\n", hostname);

    rh = alloc(SIZEOF(*rh));
    security_handleinit(&rh->sech, &ssh_security_driver);
    rh->hostname = NULL;
    rh->rs = NULL;
    rh->ev_timeout = NULL;
    rh->rc = NULL;

    /* get the canonical hostname */
    rh->hostname = NULL;
    if ((result = resolve_hostname(hostname, 0, NULL, &rh->hostname)) != 0
	 || rh->hostname == NULL) {
	security_seterror(&rh->sech,
	    _("ssh_security could not find canonical name for '%s': %s"),
	    hostname, gai_strerror(result));
	(*fn)(arg, &rh->sech, S_ERROR);
	return;
    }
    rh->rs = tcpma_stream_client(rh, newhandle++);
    rh->rc->conf_fn = conf_fn;
    rh->rc->datap = datap;

    if (rh->rs == NULL)
	goto error;

    amfree(rh->hostname);
    rh->hostname = stralloc(rh->rs->rc->hostname);

    /*
     * We need to open a new connection.
     *
     * XXX need to eventually limit number of outgoing connections here.
     */
    if(conf_fn) {
	amandad_path    = conf_fn("amandad_path", datap);
	client_username = conf_fn("client_username", datap);
	ssh_keys        = conf_fn("ssh_keys", datap);
    }
    if(rh->rc->read == -1) {
	if (runssh(rh->rs->rc, amandad_path, client_username, ssh_keys) < 0) {
	    security_seterror(&rh->sech, _("can't connect to %s: %s"),
			      hostname, rh->rs->rc->errmsg);
	    goto error;
	}
	rh->rc->refcnt++;
    }

    /*
     * The socket will be opened async so hosts that are down won't
     * block everything.  We need to register a write event
     * so we will know when the socket comes alive.
     *
     * Overload rh->rs->ev_read to provide a write event handle.
     * We also register a timeout.
     */
    rh->fn.connect = fn;
    rh->arg = arg;
    rh->rs->ev_read = event_register((event_id_t)rh->rs->rc->write, EV_WRITEFD,
	sec_connect_callback, rh);
    rh->ev_timeout = event_register((event_id_t)CONNECT_TIMEOUT, EV_TIME,
	sec_connect_timeout, rh);

    return;

error:
    (*fn)(arg, &rh->sech, S_ERROR);
}
예제 #3
0
/*
 * ssh version of a security handle allocator.  Logically sets
 * up a network "connection".
 */
static void
ssh_connect(
    const char *	hostname,
    char *		(*conf_fn)(char *, void *),
    void		(*fn)(void *, security_handle_t *, security_status_t),
    void *		arg,
    void *		datap)
{
    struct sec_handle *rh;
    char *amandad_path=NULL, *client_username=NULL, *ssh_keys=NULL;
    char *client_port = NULL;

    assert(fn != NULL);
    assert(hostname != NULL);

    auth_debug(1, "ssh_connect: %s\n", hostname);

    rh = g_new0(struct sec_handle, 1);
    security_handleinit(&rh->sech, &ssh_security_driver);
    rh->dle_hostname = g_strdup(hostname);
    rh->hostname = NULL;
    rh->rs = NULL;
    rh->ev_timeout = NULL;
    rh->rc = NULL;

    rh->hostname = g_strdup(hostname);
    rh->rs = tcpma_stream_client(rh, newhandle++);
    if (rh->rc == NULL)
	goto error;
    rh->rc->conf_fn = conf_fn;
    rh->rc->datap = datap;

    if (rh->rs == NULL)
	goto error;

    amfree(rh->hostname);
    rh->hostname = g_strdup(rh->rs->rc->hostname);

    /*
     * We need to open a new connection.
     *
     * XXX need to eventually limit number of outgoing connections here.
     */
    if(conf_fn) {
	char *port_str;
	amandad_path    = conf_fn("amandad_path", datap);
	client_username = conf_fn("client_username", datap);
	ssh_keys        = conf_fn("ssh_keys", datap);
	port_str        = conf_fn("client_port", datap);
	if (port_str && strlen(port_str) >= 1) {
	    client_port = port_str;
	}
    }
    if(rh->rc->read == -1) {
	if (runssh(rh->rs->rc, amandad_path, client_username, ssh_keys,
		   client_port) < 0) {
	    security_seterror(&rh->sech, _("can't connect to %s: %s"),
			      hostname, rh->rs->rc->errmsg);
	    goto error;
	}
	rh->rc->refcnt++;
    }

    /*
     * The socket will be opened async so hosts that are down won't
     * block everything.  We need to register a write event
     * so we will know when the socket comes alive.
     *
     * Overload rh->rs->ev_read to provide a write event handle.
     * We also register a timeout.
     */
    rh->fn.connect = fn;
    rh->arg = arg;
    rh->rs->rc->ev_write = event_register((event_id_t)rh->rs->rc->write, EV_WRITEFD,
	sec_connect_callback, rh);
    rh->ev_timeout = event_register((event_id_t)CONNECT_TIMEOUT, EV_TIME,
	sec_connect_timeout, rh);

    return;

error:
    (*fn)(arg, &rh->sech, S_ERROR);
}
예제 #4
0
/*
 * ssl version of a security handle allocator.  Logically sets
 * up a network "connection".
 */
static void
ssl_connect(
    const char *hostname,
    char *	(*conf_fn)(char *, void *),
    void	(*fn)(void *, security_handle_t *, security_status_t),
    void *	arg,
    void *	datap)
{
    struct sec_handle *rh;
    int result;
    char *canonname;
    char *service;
    in_port_t port;
    char *src_ip = NULL;
    char *ssl_dir = NULL;
    char *ssl_fingerprint_file = NULL;
    char *ssl_cert_file = NULL;
    char *ssl_key_file = NULL;
    char *ssl_ca_cert_file = NULL;
    char *ssl_cipher_list = NULL;
    int   ssl_check_certificate_host = 1;

    assert(fn != NULL);
    assert(hostname != NULL);

    auth_debug(1, _("ssl: ssl_connect: %s\n"), hostname);

    rh = g_new0(struct sec_handle, 1);
    security_handleinit(&rh->sech, &ssl_security_driver);
    rh->hostname = NULL;
    rh->rs = NULL;
    rh->ev_timeout = NULL;
    rh->rc = NULL;

    result = resolve_hostname(hostname, 0, NULL, &canonname);
    if(result != 0) {
	g_debug(_("resolve_hostname(%s): %s"), hostname, gai_strerror(result));
	security_seterror(&rh->sech, _("resolve_hostname(%s): %s\n"), hostname,
			  gai_strerror(result));
	(*fn)(arg, &rh->sech, S_ERROR);
	return;
    }
    if (canonname == NULL) {
	g_debug(_("resolve_hostname(%s) did not return a canonical name"), hostname);
	security_seterror(&rh->sech,
	        _("resolve_hostname(%s) did not return a canonical name\n"), hostname);
	(*fn)(arg, &rh->sech, S_ERROR);
       return;
    }

    rh->hostname = canonname;	/* will be replaced */
    canonname = NULL; /* steal reference */
    rh->rs = tcpma_stream_client(rh, newhandle++);
    rh->rc->recv_security_ok = &bsd_recv_security_ok;
    rh->rc->prefix_packet = &bsd_prefix_packet;
    rh->rc->need_priv_port = 0;

    if (rh->rs == NULL)
	goto error;

    amfree(rh->hostname);
    rh->hostname = g_strdup(rh->rs->rc->hostname);

    ssl_dir = getconf_str(CNF_SSL_DIR);
    if (conf_fn) {
	service = conf_fn("remote_port", datap);
	if (!service || strlen(service) <= 1)
	    service = AMANDA_SERVICE_NAME;
	g_debug("Connecting to service '%s'", service);
	src_ip = conf_fn("src_ip", datap);
	ssl_fingerprint_file = g_strdup(conf_fn("ssl_fingerprint_file", datap));
	ssl_cert_file        = g_strdup(conf_fn("ssl_cert_file", datap));
	ssl_key_file         = g_strdup(conf_fn("ssl_key_file", datap));
	ssl_ca_cert_file     = g_strdup(conf_fn("ssl_ca_cert_file", datap));
	ssl_cipher_list      = conf_fn("ssl_cipher_list", datap);
	ssl_check_certificate_host =
			    atoi(conf_fn("ssl_check_certificate_host", datap));
    } else {
	service = AMANDA_SERVICE_NAME;
    }

    if (ssl_dir) {
	if (!ssl_cert_file || ssl_cert_file == '\0') {
	    ssl_cert_file = g_strdup_printf("%s/me/crt.pem", ssl_dir);
	}
	if (!ssl_key_file || ssl_key_file == '\0') {
	    ssl_key_file = g_strdup_printf("%s/me/private/key.pem", ssl_dir);
	}
	if (!ssl_ca_cert_file || ssl_ca_cert_file == '\0') {
	    ssl_ca_cert_file = g_strdup_printf("%s/CA/crt.pem", ssl_dir);
	}
	if (!ssl_fingerprint_file || ssl_fingerprint_file == '\0') {
	    struct stat  statbuf;
	    ssl_fingerprint_file = g_strdup_printf("%s/remote/%s/fingerprint", ssl_dir, rh->hostname);
	    if (stat(ssl_fingerprint_file, &statbuf) == -1) {
		g_free(ssl_fingerprint_file);
		ssl_fingerprint_file = NULL;
	    }
	}
    }

    port = find_port_for_service(service, "tcp");
    if (port == 0) {
	security_seterror(&rh->sech, _("%s/tcp unknown protocol"), service);
	goto error;
    }

    /*
     * We need to open a new connection.
     */
    if(rh->rc->read == -1) {
	if (runssl(rh, port, src_ip, ssl_fingerprint_file,
                   ssl_cert_file, ssl_key_file,
		   ssl_ca_cert_file, ssl_cipher_list,
		   ssl_check_certificate_host) < 0)
	    goto error;
	rh->rc->refcnt++;
    }

    g_free(ssl_fingerprint_file);
    g_free(ssl_cert_file);
    g_free(ssl_key_file);
    g_free(ssl_ca_cert_file);
    /*
     * The socket will be opened async so hosts that are down won't
     * block everything.  We need to register a write event
     * so we will know when the socket comes alive.
     *
     * Overload rh->rs->ev_read to provide a write event handle.
     * We also register a timeout.
     */
    rh->fn.connect = fn;
    rh->arg = arg;
    rh->rs->ev_read = event_register((event_id_t)(rh->rs->rc->write),
	EV_WRITEFD, sec_connect_callback, rh);
    rh->ev_timeout = event_register(CONNECT_TIMEOUT, EV_TIME,
	sec_connect_timeout, rh);

    return;

error:
    (*fn)(arg, &rh->sech, S_ERROR);
}
예제 #5
0
/*
 * bsdtcp version of a security handle allocator.  Logically sets
 * up a network "connection".
 */
static void
bsdtcp_connect(
    const char *hostname,
    char *	(*conf_fn)(char *, void *),
    void	(*fn)(void *, security_handle_t *, security_status_t),
    void *	arg,
    void *	datap)
{
    struct sec_handle *rh;
    int result;
    char *canonname;
    char *service;
    in_port_t port;

    assert(fn != NULL);
    assert(hostname != NULL);
    (void)conf_fn;	/* Quiet unused parameter warning */
    (void)datap;	/* Quiet unused parameter warning */

    auth_debug(1, _("bsdtcp: bsdtcp_connect: %s\n"), hostname);

    rh = alloc(sizeof(*rh));
    security_handleinit(&rh->sech, &bsdtcp_security_driver);
    rh->hostname = NULL;
    rh->rs = NULL;
    rh->ev_timeout = NULL;
    rh->rc = NULL;

    result = resolve_hostname(hostname, 0, NULL, &canonname);
    if(result != 0) {
	dbprintf(_("resolve_hostname(%s): %s\n"), hostname, gai_strerror(result));
	security_seterror(&rh->sech, _("resolve_hostname(%s): %s\n"), hostname,
			  gai_strerror(result));
	(*fn)(arg, &rh->sech, S_ERROR);
	return;
    }
    if (canonname == NULL) {
	dbprintf(_("resolve_hostname(%s) did not return a canonical name\n"), hostname);
	security_seterror(&rh->sech,
	        _("resolve_hostname(%s) did not return a canonical name\n"), hostname);
	(*fn)(arg, &rh->sech, S_ERROR);
       return;
    }

    rh->hostname = canonname;	/* will be replaced */
    canonname = NULL; /* steal reference */
    rh->rs = tcpma_stream_client(rh, newhandle++);
    rh->rc->recv_security_ok = &bsd_recv_security_ok;
    rh->rc->prefix_packet = &bsd_prefix_packet;

    if (rh->rs == NULL)
	goto error;

    amfree(rh->hostname);
    rh->hostname = stralloc(rh->rs->rc->hostname);

    if (conf_fn) {
	service = conf_fn("client_port", datap);
	if (strlen(service) <= 1)
	    service = "amanda";
    } else {
	service = "amanda";
    }
    port = find_port_for_service(service, "tcp");
    if (port == 0) {
	security_seterror(&rh->sech, _("%s/tcp unknown protocol"), service);
	goto error;
    }

    /*
     * We need to open a new connection.
     *
     * XXX need to eventually limit number of outgoing connections here.
     */
    if(rh->rc->read == -1) {
	if (runbsdtcp(rh, port) < 0)
	    goto error;
	rh->rc->refcnt++;
    }

    /*
     * The socket will be opened async so hosts that are down won't
     * block everything.  We need to register a write event
     * so we will know when the socket comes alive.
     *
     * Overload rh->rs->ev_read to provide a write event handle.
     * We also register a timeout.
     */
    rh->fn.connect = fn;
    rh->arg = arg;
    rh->rs->ev_read = event_register((event_id_t)(rh->rs->rc->write),
	EV_WRITEFD, sec_connect_callback, rh);
    rh->ev_timeout = event_register(CONNECT_TIMEOUT, EV_TIME,
	sec_connect_timeout, rh);

    return;

error:
    (*fn)(arg, &rh->sech, S_ERROR);
}
예제 #6
0
/*
 * bsdtcp version of a security handle allocator.  Logically sets
 * up a network "connection".
 */
static void
bsdtcp_connect(
    const char *hostname,
    char *	(*conf_fn)(char *, void *),
    void	(*fn)(void *, security_handle_t *, security_status_t),
    void *	arg,
    void *	datap)
{
    struct sec_handle *rh;
    int result;
    char *canonname;
    char *service;
    char *src_ip;
    in_port_t port;
    struct addrinfo *res = NULL;

    assert(fn != NULL);
    assert(hostname != NULL);
    (void)conf_fn;	/* Quiet unused parameter warning */
    (void)datap;	/* Quiet unused parameter warning */

    auth_debug(1, _("bsdtcp: bsdtcp_connect: %s\n"), hostname);

    rh = g_new0(struct sec_handle, 1);
    security_handleinit(&rh->sech, &bsdtcp_security_driver);
    rh->dle_hostname = g_strdup(hostname);
    rh->hostname = NULL;
    rh->rs = NULL;
    rh->ev_timeout = NULL;
    rh->rc = NULL;

    result = resolve_hostname(hostname, SOCK_STREAM, &res, &canonname);
    if(result != 0) {
        dbprintf(_("resolve_hostname(%s): %s\n"), hostname, gai_strerror(result));
        security_seterror(&rh->sech, _("resolve_hostname(%s): %s"), hostname,
                          gai_strerror(result));
        (*fn)(arg, &rh->sech, S_ERROR);
        if (res) freeaddrinfo(res);
        return;
    }
    if (canonname == NULL) {
        dbprintf(_("resolve_hostname(%s) did not return a canonical name\n"), hostname);
        security_seterror(&rh->sech,
                          _("resolve_hostname(%s) did not return a canonical name"), hostname);
        (*fn)(arg, &rh->sech, S_ERROR);
        if (res) freeaddrinfo(res);
        return;
    }

    rh->hostname = canonname;	/* will be replaced */
    canonname = NULL; /* steal reference */
    rh->rs = tcpma_stream_client(rh, newhandle++);
    if (rh->rc == NULL)
        goto error;

    rh->rc->recv_security_ok = &bsd_recv_security_ok;
    rh->rc->prefix_packet = &bsd_prefix_packet;
    rh->rc->need_priv_port = 1;

    if (rh->rs == NULL)
        goto error;

    amfree(rh->hostname);
    rh->hostname = g_strdup(rh->rs->rc->hostname);

    if (conf_fn) {
        service = conf_fn("client_port", datap);
        if (!service || strlen(service) <= 1)
            service = AMANDA_SERVICE_NAME;
        src_ip = conf_fn("src_ip", datap);
    } else {
        service = AMANDA_SERVICE_NAME;
        src_ip = NULL;
    }
    port = find_port_for_service(service, "tcp");
    if (port == 0) {
        security_seterror(&rh->sech, _("%s/tcp unknown protocol"), service);
        goto error;
    }

    /*
     * We need to open a new connection.
     *
     * XXX need to eventually limit number of outgoing connections here.
     */
    rh->res = res;
    rh->next_res = res;
    rh->src_ip = src_ip;
    rh->port = port;
    if(rh->rc->read == -1) {
        int result = -1;
        while (rh->next_res) {
            result = runbsdtcp(rh, rh->src_ip, rh->port);
            if (result >=0 )
                break;
        }
        if (result < 0)
            goto error;
        rh->rc->refcnt++;
    }

    /*
     * The socket will be opened async so hosts that are down won't
     * block everything.  We need to register a write event
     * so we will know when the socket comes alive.
     *
     * Overload rh->rs->ev_read to provide a write event handle.
     * We also register a timeout.
     */
    rh->fn.connect = &bsdtcp_fn_connect;
    rh->arg = rh;
    rh->connect_callback = fn;
    rh->connect_arg = arg;
    g_mutex_lock(security_mutex);
    rh->rs->rc->ev_write = event_create((event_id_t)(rh->rs->rc->write),
                                        EV_WRITEFD, sec_connect_callback, rh);
    rh->ev_timeout = event_create(CONNECT_TIMEOUT, EV_TIME,
                                  sec_connect_timeout, rh);
    event_activate(rh->rs->rc->ev_write);
    event_activate(rh->ev_timeout);
    g_mutex_unlock(security_mutex);

    return;

error:
    if (res) {
        freeaddrinfo(res);
    }
    rh->res = NULL;
    rh->next_res = NULL;
    (*fn)(arg, &rh->sech, S_ERROR);
}
예제 #7
0
/*
 * local version of a security handle allocator.  Logically sets
 * up a network "connection".
 */
static void
local_connect(
    const char *	hostname,
    char *		(*conf_fn)(char *, void *),
    void		(*fn)(void *, security_handle_t *, security_status_t),
    void *		arg,
    void *		datap)
{
    struct sec_handle *rh;
    char *amandad_path=NULL;
    char *client_username=NULL;
    char myhostname[MAX_HOSTNAME_LENGTH+1];

    assert(fn != NULL);
    assert(hostname != NULL);

    auth_debug(1, _("local: local_connect: %s\n"), hostname);

    rh = g_new0(struct sec_handle, 1);
    security_handleinit(&rh->sech, &local_security_driver);
    rh->hostname = NULL;
    rh->rs = NULL;
    rh->ev_timeout = NULL;
    rh->rc = NULL;

    if (gethostname(myhostname, MAX_HOSTNAME_LENGTH) == -1) {
	security_seterror(&rh->sech, _("gethostname failed"));
	(*fn)(arg, &rh->sech, S_ERROR);
	return;
    }
    myhostname[SIZEOF(myhostname)-1] = '\0';

    if (strcmp(hostname, myhostname) != 0 &&
	match("^localhost(\\.localdomain)?$", hostname) == 0) {
	security_seterror(&rh->sech,
	    _("%s: is not local"), hostname);
	(*fn)(arg, &rh->sech, S_ERROR);
	return;
    }
    rh->hostname = stralloc(hostname);
    rh->rs = tcpma_stream_client(rh, newhandle++);

    if (rh->rs == NULL)
	goto error;

    amfree(rh->hostname);
    rh->hostname = stralloc(rh->rs->rc->hostname);

    /*
     * We need to open a new connection.
     *
     * XXX need to eventually limit number of outgoing connections here.
     */
    if(conf_fn) {
	amandad_path    = conf_fn("amandad_path", datap);
	client_username = conf_fn("client_username", datap);
    }
    if(rh->rc->read == -1) {
	if (runlocal(rh->rs->rc, amandad_path, client_username) < 0) {
	    security_seterror(&rh->sech, _("can't connect to %s: %s"),
			      hostname, rh->rs->rc->errmsg);
	    goto error;
	}
	rh->rc->refcnt++;
    }

    /*
     * The socket will be opened async so hosts that are down won't
     * block everything.  We need to register a write event
     * so we will know when the socket comes alive.
     *
     * Overload rh->rs->ev_read to provide a write event handle.
     * We also register a timeout.
     */
    rh->fn.connect = fn;
    rh->arg = arg;
    rh->rs->ev_read = event_register((event_id_t)rh->rs->rc->write, EV_WRITEFD,
	sec_connect_callback, rh);
    rh->ev_timeout = event_register((event_id_t)CONNECT_TIMEOUT, EV_TIME,
	sec_connect_timeout, rh);

    return;

error:
    (*fn)(arg, &rh->sech, S_ERROR);
    amfree(rh->hostname);
}